Middleware Nedir? Frontend ve Backend'de Kullanımı - Kapsamlı Rehber

Hamza İnce
2025-02-05
45 dakika
0%

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.

Etiketler:
MiddlewareExpress.jsNext.jsNode.jsWeb SecurityPerformanceAuthenticationRate LimitingCachingValidation