← Blog
Yazı
· 45 dakika· Web Development

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.