Middleware Nedir? Frontend ve Backend'de Kullanımı - Kapsamlı Rehber
Middleware, modern web uygulamalarının vazgeçilmez bileşenlerinden biridir. Bu kapsamlı rehberde, middleware'in ne olduğunu, nasıl çalıştığını, farklı platformlardaki kullanım alanlarını ve pratik örneklerini detaylıca inceleyeceğiz.
🔧 Middleware Nedir?
Middleware, istemci (client) ile sunucu (server) arasında veya uygulama katmanları arasında köprü görevi gören ara yazılımlardır. Gelen istekleri işlemek, dönüştürmek, filtrelemek veya yönlendirmek için kullanılır.
Middleware: Bir istek-cevap döngüsünde, uygulamanın farklı katmanları arasında veri ve işlem akışını yöneten ara katman yazılımlardır.
🏗️ Middleware'in Çalışma Prensibi
Middleware, genellikle şu şekilde çalışır:
1. İstek-Cevap Döngüsü
// Temel middleware yapısı
function middleware(request, response, next) {
// İstek öncesi işlemler
console.log('İstek alındı:', request.url);
// Bir sonraki middleware'e geç
next();
// Cevap sonrası işlemler (opsiyonel)
console.log('Cevap gönderildi');
}
// Express.js'de kullanım
app.use(middleware);
2. Middleware Zincirleme
// Birden fazla middleware zincirleme
const express = require('express');
const app = express();
// 1. Middleware - Loglama
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
});
// 2. Middleware - Kimlik doğrulama
app.use((req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Token gerekli' });
}
// Token doğrulama
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Geçersiz token' });
}
});
// 3. Middleware - İzin kontrolü
app.use((req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Yetki gerekli' });
}
next();
});
// Route handler
app.get('/admin/users', (req, res) => {
res.json({ users: ['user1', 'user2'] });
});
Önemli: Middleware'lerin tanımlandığı sıra kritiktir. Her middleware, bir sonrakini çağırmak için next() fonksiyonunu kullanmalıdır.
🌐 Backend'de Middleware Kullanımı
1. Express.js Middleware
// Express.js'de kapsamlı middleware örneği
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');
const app = express();
// Güvenlik middleware'i
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
}));
// CORS middleware'i
app.use(cors({
origin: ['http://localhost:3000', 'https://myapp.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
// Rate limiting middleware'i
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 dakika
max: 100, // IP başına 100 istek
message: {
error: 'Çok fazla istek gönderildi, lütfen bekleyin'
},
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// Request logging middleware'i
app.use(morgan('combined', {
stream: {
write: (message) => {
console.log(message.trim());
}
}
}));
// JSON parsing middleware'i
app.use(express.json({
limit: '10mb',
verify: (req, res, buf) => {
// Request body'yi raw olarak sakla
req.rawBody = buf;
}
}));
// URL encoded parsing middleware'i
app.use(express.urlencoded({
extended: true,
limit: '10mb'
}));
// Custom authentication middleware
const authenticateToken = async (req, res, next) => {
try {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({
error: 'Erişim token'ı gerekli'
});
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Kullanıcı bilgilerini veritabanından al
const user = await User.findById(decoded.userId);
if (!user) {
return res.status(401).json({
error: 'Kullanıcı bulunamadı'
});
}
req.user = user;
next();
} catch (error) {
return res.status(403).json({
error: 'Geçersiz token'
});
}
};
// Error handling middleware
const errorHandler = (err, req, res, next) => {
console.error('Error:', err);
// Mongoose validation error
if (err.name === 'ValidationError') {
const errors = Object.values(err.errors).map(e => e.message);
return res.status(400).json({
error: 'Validation Error',
details: errors
});
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({
error: 'Geçersiz token'
});
}
// Duplicate key error
if (err.code === 11000) {
return res.status(400).json({
error: 'Duplicate field value entered'
});
}
// Default error
res.status(err.statusCode || 500).json({
error: err.message || 'Server Error'
});
};
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/users', authenticateToken, userRoutes);
app.use('/api/admin', authenticateToken, adminMiddleware, adminRoutes);
// Error handling middleware (en sonda olmalı)
app.use(errorHandler);
module.exports = app;
2. Next.js Middleware
// Next.js 13+ middleware.js
import { NextResponse } from 'next/server';
import { verify } from 'jsonwebtoken';
export function middleware(request) {
const { pathname } = request.nextUrl;
// Public routes
const publicRoutes = ['/login', '/register', '/forgot-password'];
if (publicRoutes.includes(pathname)) {
return NextResponse.next();
}
// API routes middleware
if (pathname.startsWith('/api/')) {
return handleAPIMiddleware(request);
}
// Protected routes middleware
if (pathname.startsWith('/dashboard')) {
return handleAuthMiddleware(request);
}
// Admin routes middleware
if (pathname.startsWith('/admin')) {
return handleAdminMiddleware(request);
}
return NextResponse.next();
}
function handleAPIMiddleware(request) {
const response = NextResponse.next();
// CORS headers
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Rate limiting (basit örnek)
const ip = request.ip || 'unknown';
const rateLimitKey = `rate_limit_${ip}`;
// Gerçek uygulamada Redis kullanılabilir
const requestCount = getRequestCount(rateLimitKey);
if (requestCount > 100) {
return new NextResponse(
JSON.stringify({ error: 'Rate limit exceeded' }),
{ status: 429, headers: { 'Content-Type': 'application/json' } }
);
}
incrementRequestCount(rateLimitKey);
return response;
}
function handleAuthMiddleware(request) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
try {
const payload = verify(token, process.env.JWT_SECRET);
// Token'ı request header'a ekle
const response = NextResponse.next();
response.headers.set('x-user-id', payload.userId);
response.headers.set('x-user-role', payload.role);
return response;
} catch (error) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
function handleAdminMiddleware(request) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
try {
const payload = verify(token, process.env.JWT_SECRET);
if (payload.role !== 'admin') {
return NextResponse.redirect(new URL('/unauthorized', request.url));
}
return NextResponse.next();
} catch (error) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
// Rate limiting helper functions (örnek implementasyon)
const requestCounts = new Map();
function getRequestCount(key) {
const now = Date.now();
const windowStart = now - (15 * 60 * 1000); // 15 dakika
const requests = requestCounts.get(key) || [];
const validRequests = requests.filter(time => time > windowStart);
requestCounts.set(key, validRequests);
return validRequests.length;
}
function incrementRequestCount(key) {
const now = Date.now();
const requests = requestCounts.get(key) || [];
requests.push(now);
requestCounts.set(key, requests);
}
export const config = {
matcher: [
'/dashboard/:path*',
'/admin/:path*',
'/api/:path*'
]
};
3. Node.js Custom Middleware
// Kendi middleware framework'ümüzü oluşturma
class MiddlewareManager {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
}
async execute(context) {
let index = 0;
const next = async () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
await middleware(context, next);
}
};
await next();
}
}
// Middleware örnekleri
const logger = async (context, next) => {
const start = Date.now();
console.log(`${context.method} ${context.url} - Başladı`);
await next();
const duration = Date.now() - start;
console.log(`${context.method} ${context.url} - Tamamlandı (${duration}ms)`);
};
const authentication = async (context, next) => {
const token = context.headers.authorization;
if (!token) {
context.status = 401;
context.body = { error: 'Token gerekli' };
return;
}
try {
const user = await verifyToken(token);
context.user = user;
await next();
} catch (error) {
context.status = 401;
context.body = { error: 'Geçersiz token' };
}
};
const validation = (schema) => async (context, next) => {
try {
context.validatedData = await schema.validate(context.body);
await next();
} catch (error) {
context.status = 400;
context.body = {
error: 'Validation failed',
details: error.errors
};
}
};
const caching = (ttl = 300) => async (context, next) => {
const cacheKey = `cache_${context.method}_${context.url}`;
// Cache'den kontrol et
const cached = await cache.get(cacheKey);
if (cached) {
context.body = JSON.parse(cached);
return;
}
await next();
// Başarılı response'u cache'le
if (context.status === 200) {
await cache.set(cacheKey, JSON.stringify(context.body), ttl);
}
};
// Kullanım
const app = new MiddlewareManager();
app.use(logger);
app.use(authentication);
app.use(validation(userSchema));
app.use(caching(600));
// Handler
app.use(async (context, next) => {
// Ana işlem logic'i
context.body = { message: 'Success', data: context.validatedData };
});
Backend middleware'ler: Kod tekrarını önler, cross-cutting concerns'i merkezi olarak yönetir ve uygulamanın modülerliğini artırır.
💻 Frontend'de Middleware Kullanımı
1. Redux Middleware
// Redux middleware örnekleri
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// Custom logging middleware
const loggerMiddleware = (store) => (next) => (action) => {
console.group(action.type);
console.info('Dispatching:', action);
console.log('Previous state:', store.getState());
const result = next(action);
console.log('Next state:', store.getState());
console.groupEnd();
return result;
};
// Error handling middleware
const errorMiddleware = (store) => (next) => (action) => {
try {
return next(action);
} catch (error) {
console.error('Redux Error:', error);
// Error'ı state'e kaydet
store.dispatch({
type: 'SET_ERROR',
payload: {
message: error.message,
stack: error.stack,
action: action.type
}
});
throw error;
}
};
// API middleware
const apiMiddleware = (store) => (next) => (action) => {
if (action.type !== 'API_CALL') {
return next(action);
}
const { endpoint, method, body, onSuccess, onError } = action.payload;
// Loading başlat
store.dispatch({ type: 'SET_LOADING', payload: true });
fetch(endpoint, {
method,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${store.getState().auth.token}`
},
body: body ? JSON.stringify(body) : undefined
})
.then(response => response.json())
.then(data => {
store.dispatch({ type: 'SET_LOADING', payload: false });
if (onSuccess) {
store.dispatch({ type: onSuccess, payload: data });
}
})
.catch(error => {
store.dispatch({ type: 'SET_LOADING', payload: false });
if (onError) {
store.dispatch({ type: onError, payload: error.message });
}
});
};
// Persistence middleware
const persistenceMiddleware = (store) => (next) => (action) => {
const result = next(action);
// Belirli action'ları localStorage'a kaydet
const persistedActions = ['SET_USER', 'SET_PREFERENCES', 'SET_THEME'];
if (persistedActions.includes(action.type)) {
const state = store.getState();
localStorage.setItem('appState', JSON.stringify({
user: state.user,
preferences: state.preferences,
theme: state.theme
}));
}
return result;
};
// Store oluşturma
const store = createStore(
rootReducer,
applyMiddleware(
thunk,
loggerMiddleware,
errorMiddleware,
apiMiddleware,
persistenceMiddleware
)
);
2. Axios Interceptors (HTTP Middleware)
// Axios interceptors ile HTTP middleware
import axios from 'axios';
// Request interceptor
axios.interceptors.request.use(
(config) => {
// Loading başlat
store.dispatch({ type: 'SET_LOADING', payload: true });
// Token ekle
const token = localStorage.getItem('auth-token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// Request ID ekle
config.headers['X-Request-ID'] = generateRequestId();
// Timestamp ekle
config.metadata = { startTime: Date.now() };
console.log('HTTP Request:', config.method?.toUpperCase(), config.url);
return config;
},
(error) => {
store.dispatch({ type: 'SET_LOADING', payload: false });
return Promise.reject(error);
}
);
// Response interceptor
axios.interceptors.response.use(
(response) => {
// Loading durdur
store.dispatch({ type: 'SET_LOADING', payload: false });
// Response time hesapla
const duration = Date.now() - response.config.metadata.startTime;
console.log(
`HTTP Response: ${response.status} (${duration}ms)`,
response.config.url
);
// Başarılı response için analytics
analytics.track('API_SUCCESS', {
endpoint: response.config.url,
method: response.config.method,
duration,
status: response.status
});
return response;
},
(error) => {
store.dispatch({ type: 'SET_LOADING', payload: false });
const { response, config } = error;
// Token yenileme logic'i
if (response?.status === 401) {
const refreshToken = localStorage.getItem('refresh-token');
if (refreshToken) {
return refreshAuthToken()
.then((newToken) => {
localStorage.setItem('auth-token', newToken);
config.headers.Authorization = `Bearer ${newToken}`;
return axios.request(config);
})
.catch(() => {
// Refresh token da geçersiz, logout yap
store.dispatch({ type: 'LOGOUT' });
window.location.href = '/login';
});
} else {
store.dispatch({ type: 'LOGOUT' });
window.location.href = '/login';
}
}
// Error notification
if (response?.status >= 500) {
store.dispatch({
type: 'SHOW_NOTIFICATION',
payload: {
type: 'error',
message: 'Sunucu hatası oluştu, lütfen tekrar deneyin'
}
});
}
// Error analytics
analytics.track('API_ERROR', {
endpoint: config?.url,
method: config?.method,
status: response?.status,
error: error.message
});
return Promise.reject(error);
}
);
// Custom API client
class ApiClient {
constructor(baseURL) {
this.client = axios.create({ baseURL });
this.setupInterceptors();
}
setupInterceptors() {
// Request timeout middleware
this.client.interceptors.request.use((config) => {
config.timeout = config.timeout || 10000; // 10 saniye
return config;
});
// Retry middleware
this.client.interceptors.response.use(
(response) => response,
(error) => {
const { config } = error;
if (!config.retryCount) {
config.retryCount = 0;
}
const shouldRetry =
config.retryCount < 3 &&
error.response?.status >= 500;
if (shouldRetry) {
config.retryCount++;
const delay = Math.pow(2, config.retryCount) * 1000; // Exponential backoff
return new Promise((resolve) => {
setTimeout(() => resolve(this.client(config)), delay);
});
}
return Promise.reject(error);
}
);
}
async get(url, options = {}) {
return this.client.get(url, options);
}
async post(url, data, options = {}) {
return this.client.post(url, data, options);
}
async put(url, data, options = {}) {
return this.client.put(url, data, options);
}
async delete(url, options = {}) {
return this.client.delete(url, options);
}
}
const apiClient = new ApiClient('https://api.example.com');
3. React Router Middleware
// React Router ile navigation middleware
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
// Route guard middleware
export function RouteGuard({ children, requiredRole = null }) {
const navigate = useNavigate();
const location = useLocation();
const { user, isAuthenticated } = useAuth();
useEffect(() => {
// Authentication kontrolü
if (!isAuthenticated) {
navigate('/login', {
state: { from: location.pathname }
});
return;
}
// Role kontrolü
if (requiredRole && user.role !== requiredRole) {
navigate('/unauthorized');
return;
}
// Analytics
analytics.track('PAGE_VIEW', {
path: location.pathname,
user: user?.id
});
}, [isAuthenticated, user, location.pathname, navigate, requiredRole]);
if (!isAuthenticated) {
return Redirecting...;
}
if (requiredRole && user.role !== requiredRole) {
return Unauthorized access;
}
return children;
}
// Navigation analytics middleware
export function NavigationMiddleware() {
const location = useLocation();
useEffect(() => {
// Page view tracking
const startTime = Date.now();
// SEO optimization
document.title = getPageTitle(location.pathname);
// Loading state
document.body.classList.add('page-loading');
const timer = setTimeout(() => {
document.body.classList.remove('page-loading');
}, 100);
// Cleanup function
return () => {
clearTimeout(timer);
// Time on page tracking
const timeOnPage = Date.now() - startTime;
analytics.track('TIME_ON_PAGE', {
path: location.pathname,
duration: timeOnPage
});
};
}, [location.pathname]);
return null;
}
// Custom hook for route-based data fetching
export function useRouteData(routeConfig) {
const location = useLocation();
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const config = routeConfig[location.pathname];
if (!config) {
setData(null);
return;
}
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const result = await config.loader();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [location.pathname, routeConfig]);
return { data, loading, error };
}
// Route configuration
const routeConfig = {
'/dashboard': {
loader: () => fetch('/api/dashboard').then(r => r.json()),
title: 'Dashboard'
},
'/profile': {
loader: () => fetch('/api/profile').then(r => r.json()),
title: 'Profile'
}
};
// App component
function App() {
return (
} />
}
/>
}
/>
);
}
Frontend middleware'ler: State yönetimi, HTTP istekleri, routing ve kullanıcı deneyimi gibi alanlarda merkezi kontrol sağlar.
🔄 Middleware Türleri ve Kullanım Alanları
1. Authentication Middleware
// Gelişmiş authentication middleware
class AuthenticationMiddleware {
constructor(options = {}) {
this.tokenStorage = options.tokenStorage || localStorage;
this.refreshEndpoint = options.refreshEndpoint || '/api/auth/refresh';
this.loginEndpoint = options.loginEndpoint || '/login';
}
async handle(request, response, next) {
const token = this.getToken(request);
if (!token) {
return this.redirectToLogin(response);
}
try {
const payload = await this.verifyToken(token);
// Token süresi kontrolü
if (this.isTokenExpiring(payload)) {
await this.refreshToken(request);
}
request.user = payload;
return next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
try {
await this.refreshToken(request);
return next();
} catch (refreshError) {
return this.redirectToLogin(response);
}
}
return this.sendUnauthorized(response);
}
}
getToken(request) {
// Header'dan token al
const authHeader = request.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
// Cookie'den token al
return request.cookies['auth-token'];
}
async verifyToken(token) {
return jwt.verify(token, process.env.JWT_SECRET);
}
isTokenExpiring(payload) {
const now = Date.now() / 1000;
const timeUntilExpiry = payload.exp - now;
return timeUntilExpiry < 300; // 5 dakikadan az kaldıysa
}
async refreshToken(request) {
const refreshToken = request.cookies['refresh-token'];
if (!refreshToken) {
throw new Error('No refresh token');
}
const response = await fetch(this.refreshEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ refreshToken })
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const { accessToken } = await response.json();
// Yeni token'ı kaydet
this.tokenStorage.setItem('auth-token', accessToken);
request.headers.authorization = `Bearer ${accessToken}`;
}
redirectToLogin(response) {
if (typeof window !== 'undefined') {
window.location.href = this.loginEndpoint;
} else {
response.redirect(this.loginEndpoint);
}
}
sendUnauthorized(response) {
return response.status(401).json({
error: 'Unauthorized access'
});
}
}
2. Caching Middleware
// Gelişmiş caching middleware
class CachingMiddleware {
constructor(options = {}) {
this.cache = options.cache || new Map();
this.defaultTTL = options.defaultTTL || 300; // 5 dakika
this.keyGenerator = options.keyGenerator || this.defaultKeyGenerator;
}
async handle(request, response, next) {
const cacheKey = this.keyGenerator(request);
const cached = await this.get(cacheKey);
if (cached && !this.isExpired(cached)) {
// Cache hit
response.setHeader('X-Cache', 'HIT');
response.setHeader('X-Cache-TTL', cached.ttl - Date.now());
return response.json(cached.data);
}
// Cache miss - execute original handler
const originalSend = response.json;
response.json = (data) => {
// Cache the response
this.set(cacheKey, data, this.getTTL(request));
response.setHeader('X-Cache', 'MISS');
return originalSend.call(response, data);
};
return next();
}
defaultKeyGenerator(request) {
const { method, url, query, user } = request;
return `${method}:${url}:${JSON.stringify(query)}:${user?.id || 'anonymous'}`;
}
async get(key) {
if (this.cache instanceof Map) {
return this.cache.get(key);
}
// Redis or other external cache
const cached = await this.cache.get(key);
return cached ? JSON.parse(cached) : null;
}
async set(key, data, ttl) {
const cacheEntry = {
data,
ttl: Date.now() + (ttl * 1000),
createdAt: Date.now()
};
if (this.cache instanceof Map) {
this.cache.set(key, cacheEntry);
// TTL cleanup
setTimeout(() => {
this.cache.delete(key);
}, ttl * 1000);
} else {
// Redis or other external cache
await this.cache.setex(key, ttl, JSON.stringify(cacheEntry));
}
}
isExpired(cached) {
return Date.now() > cached.ttl;
}
getTTL(request) {
// Route-specific TTL
const routeTTL = {
'/api/users': 60, // 1 dakika
'/api/posts': 300, // 5 dakika
'/api/stats': 3600, // 1 saat
};
return routeTTL[request.route?.path] || this.defaultTTL;
}
// Cache invalidation
async invalidate(pattern) {
if (this.cache instanceof Map) {
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key);
}
}
} else {
// Redis pattern deletion
const keys = await this.cache.keys(`*${pattern}*`);
if (keys.length > 0) {
await this.cache.del(keys);
}
}
}
}
3. Validation Middleware
// Comprehensive validation middleware
const Joi = require('joi');
class ValidationMiddleware {
constructor(schemas = {}) {
this.schemas = schemas;
}
validate(schemaName) {
return async (request, response, next) => {
const schema = this.schemas[schemaName];
if (!schema) {
return response.status(500).json({
error: `Validation schema '${schemaName}' not found`
});
}
try {
// Body validation
if (schema.body) {
request.body = await schema.body.validateAsync(request.body, {
abortEarly: false,
stripUnknown: true
});
}
// Query validation
if (schema.query) {
request.query = await schema.query.validateAsync(request.query, {
abortEarly: false,
stripUnknown: true
});
}
// Params validation
if (schema.params) {
request.params = await schema.params.validateAsync(request.params, {
abortEarly: false
});
}
// Headers validation
if (schema.headers) {
request.headers = await schema.headers.validateAsync(request.headers, {
abortEarly: false,
allowUnknown: true
});
}
return next();
} catch (error) {
const validationErrors = error.details?.map(detail => ({
field: detail.path.join('.'),
message: detail.message,
value: detail.context?.value
})) || [{ message: error.message }];
return response.status(400).json({
error: 'Validation failed',
details: validationErrors
});
}
};
}
}
// Validation schemas
const validationSchemas = {
createUser: {
body: Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
password: Joi.string().min(8).pattern(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&])[A-Za-zd@$!%*?&]/
).required().messages({
'string.pattern.base': 'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character'
}),
age: Joi.number().integer().min(18).max(120),
preferences: Joi.object({
newsletter: Joi.boolean().default(false),
notifications: Joi.boolean().default(true)
}).default({})
})
},
updateUser: {
params: Joi.object({
id: Joi.string().pattern(/^[0-9a-fA-F]{24}$/).required()
}),
body: Joi.object({
name: Joi.string().min(2).max(50),
email: Joi.string().email(),
age: Joi.number().integer().min(18).max(120),
preferences: Joi.object({
newsletter: Joi.boolean(),
notifications: Joi.boolean()
})
}).min(1) // En az bir alan güncellenmeli
},
getUsers: {
query: Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(10),
sort: Joi.string().valid('name', 'email', 'createdAt', '-name', '-email', '-createdAt').default('-createdAt'),
search: Joi.string().max(100),
status: Joi.string().valid('active', 'inactive', 'pending')
})
}
};
const validator = new ValidationMiddleware(validationSchemas);
// Usage
app.post('/api/users', validator.validate('createUser'), createUserHandler);
app.put('/api/users/:id', validator.validate('updateUser'), updateUserHandler);
app.get('/api/users', validator.validate('getUsers'), getUsersHandler);
Dikkat: Çok fazla middleware kullanımı performansı etkileyebilir. Gereksiz middleware'leri kaldırın ve kritik path'lerde optimizasyon yapın.
🛡️ Güvenlik Middleware'leri
1. Security Headers Middleware
// Comprehensive security middleware
class SecurityMiddleware {
constructor(options = {}) {
this.options = {
contentSecurityPolicy: true,
frameGuard: true,
hidePoweredBy: true,
hsts: true,
ieNoOpen: true,
noSniff: true,
xssFilter: true,
referrerPolicy: true,
...options
};
}
handle(request, response, next) {
// Content Security Policy
if (this.options.contentSecurityPolicy) {
const csp = [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://trusted-cdn.com",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https:",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; ');
response.setHeader('Content-Security-Policy', csp);
}
// X-Frame-Options
if (this.options.frameGuard) {
response.setHeader('X-Frame-Options', 'DENY');
}
// Hide X-Powered-By
if (this.options.hidePoweredBy) {
response.removeHeader('X-Powered-By');
}
// HTTP Strict Transport Security
if (this.options.hsts && request.secure) {
response.setHeader(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
}
// X-Download-Options
if (this.options.ieNoOpen) {
response.setHeader('X-Download-Options', 'noopen');
}
// X-Content-Type-Options
if (this.options.noSniff) {
response.setHeader('X-Content-Type-Options', 'nosniff');
}
// X-XSS-Protection
if (this.options.xssFilter) {
response.setHeader('X-XSS-Protection', '1; mode=block');
}
// Referrer Policy
if (this.options.referrerPolicy) {
response.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
}
// Additional security headers
response.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
response.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
next();
}
}
2. Rate Limiting Middleware
// Advanced rate limiting middleware
class RateLimitMiddleware {
constructor(options = {}) {
this.windowMs = options.windowMs || 15 * 60 * 1000; // 15 dakika
this.max = options.max || 100; // Maximum requests
this.store = options.store || new Map();
this.keyGenerator = options.keyGenerator || this.defaultKeyGenerator;
this.skipSuccessfulRequests = options.skipSuccessfulRequests || false;
this.skipFailedRequests = options.skipFailedRequests || false;
}
async handle(request, response, next) {
const key = this.keyGenerator(request);
const now = Date.now();
const windowStart = now - this.windowMs;
// Get current window data
let windowData = await this.getWindowData(key);
// Clean old requests
windowData.requests = windowData.requests.filter(
requestTime => requestTime > windowStart
);
// Check if limit exceeded
if (windowData.requests.length >= this.max) {
const oldestRequest = Math.min(...windowData.requests);
const timeUntilReset = this.windowMs - (now - oldestRequest);
response.setHeader('X-RateLimit-Limit', this.max);
response.setHeader('X-RateLimit-Remaining', 0);
response.setHeader('X-RateLimit-Reset', new Date(now + timeUntilReset));
response.setHeader('Retry-After', Math.ceil(timeUntilReset / 1000));
return response.status(429).json({
error: 'Too many requests',
message: `Rate limit exceeded. Try again in ${Math.ceil(timeUntilReset / 1000)} seconds.`
});
}
// Add current request
windowData.requests.push(now);
await this.setWindowData(key, windowData);
// Set rate limit headers
response.setHeader('X-RateLimit-Limit', this.max);
response.setHeader('X-RateLimit-Remaining', this.max - windowData.requests.length);
response.setHeader('X-RateLimit-Reset', new Date(now + this.windowMs));
// Hook into response to conditionally count request
if (this.skipSuccessfulRequests || this.skipFailedRequests) {
const originalSend = response.send;
response.send = function(body) {
const shouldSkip =
(this.skipSuccessfulRequests && response.statusCode < 400) ||
(this.skipFailedRequests && response.statusCode >= 400);
if (shouldSkip) {
// Remove the request from count
windowData.requests.pop();
this.setWindowData(key, windowData);
}
return originalSend.call(response, body);
}.bind(this);
}
next();
}
defaultKeyGenerator(request) {
return request.ip || request.connection.remoteAddress;
}
async getWindowData(key) {
if (this.store instanceof Map) {
return this.store.get(key) || { requests: [] };
} else {
// Redis implementation
const data = await this.store.get(key);
return data ? JSON.parse(data) : { requests: [] };
}
}
async setWindowData(key, data) {
if (this.store instanceof Map) {
this.store.set(key, data);
} else {
// Redis implementation with expiration
await this.store.setex(
key,
Math.ceil(this.windowMs / 1000),
JSON.stringify(data)
);
}
}
}
📊 Monitoring ve Debugging Middleware
1. Performance Monitoring
// Performance monitoring middleware
class PerformanceMiddleware {
constructor(options = {}) {
this.slowThreshold = options.slowThreshold || 1000; // 1 saniye
this.sampleRate = options.sampleRate || 0.1; // %10 sampling
this.metrics = new Map();
}
handle(request, response, next) {
const startTime = process.hrtime.bigint();
const startMemory = process.memoryUsage();
// Request metadata
const requestId = this.generateRequestId();
const metadata = {
requestId,
method: request.method,
url: request.url,
userAgent: request.headers['user-agent'],
startTime: Date.now(),
startMemory
};
request.metadata = metadata;
// Response hook
const originalEnd = response.end;
response.end = function(...args) {
const endTime = process.hrtime.bigint();
const endMemory = process.memoryUsage();
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
const memoryDelta = {
rss: endMemory.rss - startMemory.rss,
heapUsed: endMemory.heapUsed - startMemory.heapUsed,
heapTotal: endMemory.heapTotal - startMemory.heapTotal
};
const performanceData = {
...metadata,
duration,
memoryDelta,
statusCode: response.statusCode,
contentLength: response.getHeader('content-length') || 0
};
// Log slow requests
if (duration > this.slowThreshold) {
console.warn('Slow request detected:', {
requestId,
method: request.method,
url: request.url,
duration: `${duration.toFixed(2)}ms`,
statusCode: response.statusCode
});
}
// Sample and store metrics
if (Math.random() < this.sampleRate) {
this.storeMetrics(performanceData);
}
// Set performance headers
response.setHeader('X-Response-Time', `${duration.toFixed(2)}ms`);
response.setHeader('X-Request-ID', requestId);
return originalEnd.apply(response, args);
}.bind(this);
next();
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
storeMetrics(data) {
const minute = Math.floor(data.startTime / 60000) * 60000;
const key = `${data.method}:${data.url}:${minute}`;
if (!this.metrics.has(key)) {
this.metrics.set(key, {
count: 0,
totalDuration: 0,
minDuration: Infinity,
maxDuration: 0,
errors: 0,
totalMemory: 0
});
}
const metrics = this.metrics.get(key);
metrics.count++;
metrics.totalDuration += data.duration;
metrics.minDuration = Math.min(metrics.minDuration, data.duration);
metrics.maxDuration = Math.max(metrics.maxDuration, data.duration);
metrics.totalMemory += data.memoryDelta.heapUsed;
if (data.statusCode >= 400) {
metrics.errors++;
}
}
getMetrics() {
const result = {};
for (const [key, metrics] of this.metrics.entries()) {
const [method, url, timestamp] = key.split(':');
result[key] = {
method,
url,
timestamp: parseInt(timestamp),
requestCount: metrics.count,
averageDuration: metrics.totalDuration / metrics.count,
minDuration: metrics.minDuration,
maxDuration: metrics.maxDuration,
errorRate: metrics.errors / metrics.count,
averageMemoryUsage: metrics.totalMemory / metrics.count
};
}
return result;
}
}
2. Debug Middleware
// Comprehensive debug middleware
class DebugMiddleware {
constructor(options = {}) {
this.enabled = options.enabled || process.env.NODE_ENV === 'development';
this.logLevel = options.logLevel || 'info';
this.sensitiveFields = options.sensitiveFields || [
'password', 'token', 'secret', 'key', 'authorization'
];
}
handle(request, response, next) {
if (!this.enabled) {
return next();
}
const requestData = this.sanitizeRequest(request);
console.group(`🔍 ${request.method} ${request.url}`);
console.log('📥 Request:', {
headers: requestData.headers,
query: requestData.query,
params: requestData.params,
body: requestData.body,
cookies: requestData.cookies,
ip: request.ip,
timestamp: new Date().toISOString()
});
// Response interceptor
const originalSend = response.send;
const originalJson = response.json;
response.send = function(body) {
console.log('📤 Response:', {
statusCode: response.statusCode,
headers: response.getHeaders(),
body: this.sanitizeData(body),
timestamp: new Date().toISOString()
});
console.groupEnd();
return originalSend.call(response, body);
}.bind(this);
response.json = function(data) {
console.log('📤 JSON Response:', {
statusCode: response.statusCode,
headers: response.getHeaders(),
data: this.sanitizeData(data),
timestamp: new Date().toISOString()
});
console.groupEnd();
return originalJson.call(response, data);
}.bind(this);
next();
}
sanitizeRequest(request) {
return {
headers: this.sanitizeData(request.headers),
query: this.sanitizeData(request.query),
params: this.sanitizeData(request.params),
body: this.sanitizeData(request.body),
cookies: this.sanitizeData(request.cookies)
};
}
sanitizeData(data) {
if (!data || typeof data !== 'object') {
return data;
}
const sanitized = Array.isArray(data) ? [] : {};
for (const [key, value] of Object.entries(data)) {
const lowerKey = key.toLowerCase();
const isSensitive = this.sensitiveFields.some(field =>
lowerKey.includes(field.toLowerCase())
);
if (isSensitive) {
sanitized[key] = '[REDACTED]';
} else if (typeof value === 'object' && value !== null) {
sanitized[key] = this.sanitizeData(value);
} else {
sanitized[key] = value;
}
}
return sanitized;
}
}
En iyi uygulamalar: Middleware'leri modüler yapın, hata yönetimini ihmal etmeyin, performans etkilerini düşünün ve güvenlik önlemlerini almayı unutmayın.
🚀 Gelişmiş Middleware Teknikleri
1. Conditional Middleware
// Conditional middleware implementation
function conditionalMiddleware(condition, middleware) {
return (request, response, next) => {
if (typeof condition === 'function' ? condition(request) : condition) {
return middleware(request, response, next);
}
next();
};
}
// Usage examples
app.use(conditionalMiddleware(
req => req.path.startsWith('/api/'),
authenticateMiddleware
));
app.use(conditionalMiddleware(
process.env.NODE_ENV === 'development',
debugMiddleware
));
app.use(conditionalMiddleware(
req => req.method === 'POST',
validationMiddleware
));
2. Async Middleware Wrapper
// Async middleware error handling wrapper
function asyncMiddleware(fn) {
return (request, response, next) => {
Promise.resolve(fn(request, response, next))
.catch(next);
};
}
// Usage
app.use(asyncMiddleware(async (req, res, next) => {
const data = await database.query('SELECT * FROM users');
req.userData = data;
next();
}));
3. Middleware Composition
// Middleware composition utility
function compose(...middlewares) {
return (request, response, finalNext) => {
let index = 0;
function next() {
if (index < middlewares.length) {
const middleware = middlewares[index++];
return middleware(request, response, next);
}
return finalNext();
}
return next();
};
}
// Usage
const authStack = compose(
corsMiddleware,
rateLimitMiddleware,
authenticationMiddleware,
authorizationMiddleware
);
app.use('/api/protected', authStack);
📚 Popüler Middleware Kütüphaneleri
Backend Middleware
- Express.js: cors, helmet, morgan, compression
- Koa.js: koa-cors, koa-helmet, koa-logger
- Fastify: fastify-cors, fastify-helmet, fastify-rate-limit
- Next.js: Built-in middleware system
Frontend Middleware
- Redux: redux-thunk, redux-saga, redux-logger
- Axios: Built-in interceptors
- React Router: Route guards, navigation middleware
- Vue Router: Navigation guards
Önerilen Middleware Stack
// Production-ready middleware stack
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
const app = express();
// Security
app.use(helmet());
// CORS
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || false,
credentials: true
}));
// Compression
app.use(compression());
// Rate limiting
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
}));
// Speed limiting
app.use(slowDown({
windowMs: 15 * 60 * 1000,
delayAfter: 50,
delayMs: 500
}));
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Custom middleware
app.use(loggingMiddleware);
app.use(authenticationMiddleware);
// Routes
app.use('/api', apiRoutes);
// Error handling
app.use(errorHandlingMiddleware);
🔮 Middleware'in Geleceği
1. Edge Computing Middleware
Edge computing ile middleware'ler kullanıcıya daha yakın konumlarda çalışacak, latency'yi azaltacak.
2. AI-Powered Middleware
Yapay zeka destekli middleware'ler, otomatik optimizasyon ve anomali tespiti yapacak.
3. Serverless Middleware
Serverless mimarilerde middleware'ler daha da önemli hale gelecek ve event-driven olacak.
Middleware evrimi: Edge computing, AI integration ve serverless architectures ile middleware'ler daha akıllı ve dağıtık hale gelecek.
📝 Sonuç
Middleware, modern web uygulamalarının omurgasını oluşturan kritik bileşenlerdir. Doğru kullanıldığında, uygulamanızın güvenliğini, performansını ve sürdürülebilirliğini önemli ölçüde artırır.
Önemli Noktalar:
- Middleware'ler istek-cevap döngüsünde ara katman görevi görür
- Cross-cutting concerns'i merkezi olarak yönetir
- Frontend ve backend'de farklı şekillerde kullanılır
- Performans ve güvenlik için kritik öneme sahiptir
- Doğru sıralama ve hata yönetimi önemlidir
Bu rehberdeki örnekleri ve best practice'leri kullanarak, middleware'lerin gücünden tam anlamıyla faydalanabilir ve daha güvenli, performanslı web uygulamaları geliştirebilirsiniz.

