Node.js Middleware Pattern: İstek İşleme Zinciri

Node.js Middleware Pattern: İstek İşleme Zinciri
Middleware pattern, Node.js ve Express.js ekosisteminin en temel tasarım kalıplarından biridir. Bir HTTP isteğinin sunucuya ulaşmasından yanıtın istemciye geri dönmesine kadar olan süreçte, istek birden fazla ara katmandan (middleware) geçer. Her middleware fonksiyonu isteği inceleyebilir, değiştirebilir, yanıt döndürebilir veya bir sonraki middleware'e iletebilir. Bu rehberde middleware pattern'inin çalışma prensiplerini, farklı middleware türlerini ve kendi middleware'lerinizi nasıl yazacağınızı kapsamlı şekilde inceleyeceğiz.
1. Middleware Nedir ve Nasıl Çalışır?
Middleware, request (req), response (res) ve next parametrelerini alan bir fonksiyondur. next() çağrıldığında kontrol bir sonraki middleware'e geçer; çağrılmazsa istek zinciri orada durur. Bu zincirleme yapı, cross-cutting concern'leri (logging, authentication, validation, error handling) modüler ve yeniden kullanılabilir şekilde uygulamanızı sağlar.
const express = require('express');
const app = express();
// 1. Uygulama düzeyinde middleware (tüm route'lara uygulanır)
app.use((req, res, next) => {
req.requestTime = Date.now();
req.requestId = crypto.randomUUID();
console.log(`[${req.requestId}] ${req.method} ${req.url} - Başladı`);
next();
});
// 2. Belirli path'e özel middleware
app.use('/api', (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey || apiKey !== process.env.API_KEY) {
return res.status(401).json({ error: 'Geçersiz API anahtarı' });
}
next();
});
// 3. Route düzeyinde middleware
const logRequest = (req, res, next) => {
console.log(`Request Body: ${JSON.stringify(req.body)}`);
next();
};
app.post('/api/users', logRequest, (req, res) => {
res.status(201).json({ message: 'Kullanıcı oluşturuldu' });
});
// 4. Response time hesaplama (res.on ile)
app.use((req, res, next) => {
const start = process.hrtime.bigint();
res.on('finish', () => {
const end = process.hrtime.bigint();
const durationMs = Number(end - start) / 1e6;
console.log(`[${req.requestId}] ${req.method} ${req.url} - ${res.statusCode} - ${durationMs.toFixed(2)}ms`);
});
next();
});
2. Yaygın Middleware Türleri ve Kullanım Senaryoları
Express uygulamalarında farklı amaçlara hizmet eden çeşitli middleware türleri vardır. Built-in middleware'ler (express.json, express.static), third-party middleware'ler (cors, helmet, morgan) ve custom middleware'ler bir arada kullanılarak güçlü bir istek işleme pipeline'ı oluşturulur.
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const app = express();
// === Built-in Middleware ===
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
// === Güvenlik Middleware'leri ===
app.use(helmet());
app.use(cors({
origin: ['https://example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}));
// === Rate Limiting ===
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 dakika
max: 100, // Maksimum 100 istek
standardHeaders: true,
legacyHeaders: false,
message: {
error: 'Çok fazla istek gönderildi, lütfen daha sonra tekrar deneyin'
}
});
app.use('/api', limiter);
// === Response Compression ===
app.use(compression({
threshold: 1024, // 1KB üzeri yanıtları sıkıştır
filter: (req, res) => {
if (req.headers['x-no-compression']) return false;
return compression.filter(req, res);
}
}));
3. Custom Authentication Middleware
Authentication middleware, korunan route'lara erişimi kontrol eder. JWT (JSON Web Token) tabanlı authentication, modern API'lerde en yaygın kullanılan yöntemdir. Middleware, Authorization header'ındaki token'ı doğrulayarak kullanıcı bilgilerini request nesnesine ekler.
const jwt = require('jsonwebtoken');
// Authentication middleware
const authenticate = (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
message: 'Erişim token\'ı gerekli'
});
}
const token = authHeader.split(' ')[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = {
id: decoded.userId,
email: decoded.email,
role: decoded.role
};
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: 'Token süresi dolmuş'
});
}
return res.status(403).json({
success: false,
message: 'Geçersiz token'
});
}
};
// Authorization middleware (rol bazlı erişim kontrolü)
const authorize = (...allowedRoles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ success: false, message: 'Önce giriş yapınız' });
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: 'Bu işlem için yetkiniz bulunmamaktadır'
});
}
next();
};
};
// Kullanım
app.get('/api/profile', authenticate, (req, res) => {
res.json({ success: true, data: req.user });
});
app.delete('/api/users/:id', authenticate, authorize('admin'), (req, res) => {
res.json({ success: true, message: 'Kullanıcı silindi' });
});
4. Error Handling Middleware
Express'te hata yakalama middleware'leri dört parametre alır: err, req, res, next. Bu middleware'ler, uygulamadaki tüm hataları merkezi bir noktada yakalar ve tutarlı hata yanıtları döndürür. Farklı hata türleri için farklı HTTP status kodları ve mesajları kullanılmalıdır.
// Özel hata sınıfı
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Async handler wrapper - try/catch tekrarını önler
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Route'larda kullanım
app.get('/api/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new AppError('Kullanıcı bulunamadı', 404);
}
res.json({ success: true, data: user });
}));
// Global error handler (en son tanımlanmalı)
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
// Mongoose CastError (geçersiz ObjectId)
if (err.name === 'CastError') {
err.message = 'Geçersiz kaynak ID formatı';
err.statusCode = 400;
}
// Mongoose Duplicate Key
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
err.message = `Bu ${field} değeri zaten kullanılıyor`;
err.statusCode = 409;
}
// Mongoose Validation Error
if (err.name === 'ValidationError') {
const messages = Object.values(err.errors).map(e => e.message);
err.message = messages.join(', ');
err.statusCode = 400;
}
console.error(`[ERROR] ${err.statusCode} - ${err.message}`);
res.status(err.statusCode).json({
success: false,
message: err.isOperational ? err.message : 'Sunucu hatası oluştu',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});
5. Middleware Composition ve Chaining
Birden fazla middleware'i birleştirerek karmaşık istek işleme pipeline'ları oluşturabilirsiniz. Bu yaklaşım, her middleware'in tek bir sorumluluğu olmasını sağlar (Single Responsibility Principle) ve middleware'lerin farklı kombinasyonlarda yeniden kullanılabilmesine olanak tanır.
Middleware'leri compose ederken sıralamaya dikkat etmek gerekir. Genel olarak şu sıralama önerilir: security middleware'ler (helmet, cors), parsing middleware'ler (json, urlencoded), logging middleware'ler (morgan), authentication middleware'ler, route handler'lar ve en son error handling middleware. Bu sıralama, her katmanın doğru çalışmasını ve güvenlik açıklarının önlenmesini sağlar.
Sonuç
Middleware pattern, Node.js uygulamalarının temel yapı taşıdır. Authentication, validation, logging, error handling ve rate limiting gibi cross-cutting concern'leri modüler ve yeniden kullanılabilir şekilde uygulamanızı sağlar. Bu rehberde ele aldığımız pattern'ler ve best practice'ler, production-ready bir Express.js uygulaması oluşturmanın temelini oluşturur. Middleware'lerinizi küçük, tek sorumluluğa sahip ve test edilebilir şekilde tasarlamak uzun vadede projenizin sürdürülebilirliğini artıracaktır.