Middleware Nedir? Frontend ve Backend'de Kullanımı - Kapsamlı Rehber
Middleware, web uygulamalarının temel taşlarından biridir. Bu kapsamlı rehberde middleware'in ne olduğunu, nasıl çalıştığını, frontend ve backend'deki kullanım alanlarını ve pratik örneklerini inceleyeceğiz.
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 <div>Redirecting...</div>;
}
if (requiredRole && user.role !== requiredRole) {
return <div>Unauthorized access</div>;
}
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 (
<Router>
<NavigationMiddleware />
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<RouteGuard>
<Dashboard />
</RouteGuard>
}
/>
<Route
path="/admin"
element={
<RouteGuard requiredRole="admin">
<AdminPanel />
</RouteGuard>
}
/>
</Routes>
</Router>
);
}
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.