Veri Bilimi Okulu

FastAPI Middleware Giriş
FastAPI Middleware Giriş
fastapi_middleware_kapak_960x640

Loading

Claude Code ile FastAPI üzerine çalışırken ilginç bir şeyle karşılaştım. Kod dosyamda @app.middleware("http") şeklinde bir decorator gördüm ve düşündüm: “Durun bir dakika, ben hep @app.get("/"), @app.post("/") gibi decorator’lar görüyordum. Bu @app.middleware("http") ne işe yarıyor?”

Soruyu sorduğumda aldığım cevap beni şaşırttı – bu decorator her HTTP isteğinde (request) çalışıyormuş, sadece belirli bir endpoint’te değil! Konuyu biraz araştırdım ve öğrendiklerimi sizlerle paylaşıyorum.

Decorator’lar Arasındaki Kritik Fark

Öncelikle şu temel farkı anlayalım: FastAPI’de kullandığımız decorator’lar aslında iki farklı amaca hizmet ediyor.

Standart Route Decorator’ları

Standart route decorator’ları (@app.get(), @app.post()) sadece belirli bir HTTP metoduna ve belirli bir path’e gelen istekleri işler [1]. Örneğin:

@app.get("/")
async def root():
    return {"message": "Hello World"}

Bu kod sadece root path’e (/) gelen GET isteklerinde çalışır. Başka bir path’e istek gelirse bu fonksiyon hiç çalışmaz.

Middleware Decorator’ı

Middleware decorator’ı (@app.middleware("http")) ise tamamen farklı çalışır – uygulamanıza gelen her HTTP isteğinde, hangi path veya metod olursa olsun çalışır [1].

@app.middleware("http")
async def log_all_requests(request: Request, call_next):
    print(f"Gelen istek: {request.url}")
    response = await call_next(request)
    return response

Bu middleware tüm isteklerinizi yakalar – /, /users, /api/products, hatta var olmayan path’ler bile!

ÖzellikRoute DecoratorMiddleware Decorator
Çalışma ZamanıSadece belirli path’teHer HTTP isteğinde
HTTP MetoduBelirli (GET, POST vb.)Tümü
Kullanım AmacıEndpoint mantığıOrtak işlemler
Çalışma SırasıMiddleware’den sonraRoute’lardan önce

Middleware Nedir ve Nasıl Çalışır?

Middleware (ara yazılım), gelen HTTP isteği ile uygulamanızın işleme mantığı arasında yer alan bir katmandır [13]. Bunu havalimanı güvenliğine benzetebiliriz: her yolcu (istek) uçağa binmeden önce güvenlikten geçmek zorundadır, sonra uçaktan inerken de yine güvenlik kontrolleri yapılır [13].

Request-Response Yaşam Döngüsü (Lifecycle)

FastAPI’de bir istek şu adımlardan geçer [1]:

  1. İstek Gelir → İstemciden (client) bir HTTP isteği gelir
  2. Middleware Başlar → Middleware fonksiyonu çalışmaya başlar
  3. İstek İşlenircall_next(request) çağrılır ve istek route handler’a gider
  4. Route Handler Çalışır → Endpoint fonksiyonunuz çalışır
  5. Response Oluşur → Endpoint bir response döner
  6. Middleware Devam Eder → Middleware kaldığı yerden devam eder
  7. Response Döner → Son response istemciye gönderilir

Middleware fonksiyonunuz hem istek işlenmeden önce hem de response dönmeden önce kod çalıştırabilir [1].

call_next() Fonksiyonu Nedir?

call_next() fonksiyonu, isteğin pipeline’da (işlem hattında) bir sonraki adıma geçmesini sağlar [6]. Bu bir sonraki adım başka bir middleware olabilir veya doğrudan route handler olabilir.

@app.middleware("http")
async def my_middleware(request: Request, call_next):
    # call_next() öncesi: İstek işlenmeden ÖNCE çalışır
    print("İstek alındı")
    
    # İsteği bir sonraki adıma gönder ve response'u bekle
    response = await call_next(request)
    
    # call_next() sonrası: Response dönmeden ÖNCE çalışır
    print("Response hazırlandı")
    
    return response

İlk Middleware Örneğimiz: Response Time Ölçümü

Hadi birlikte ilk middleware’imizi yazalım. Bu örnek her isteğin ne kadar sürede işlendiğini ölçecek ve bunu response header’ına ekleyecek [1].

import time
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    # Başlangıç zamanını kaydet
    start_time = time.perf_counter()
    
    # İsteği işle ve response'u al
    response = await call_next(request)
    
    # İşlem süresini hesapla
    process_time = time.perf_counter() - start_time
    
    # Response header'ına ekle
    response.headers["X-Process-Time"] = str(process_time)
    
    return response

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/slow")
async def slow_endpoint():
    time.sleep(2)  # 2 saniye bekle
    return {"message": "Bu yavaş bir endpoint"}

Bu örnekte time.perf_counter() kullandık çünkü bu metod bu tür hassas ölçümler için daha uygundur [1].

Şimdi bu uygulamayı çalıştırıp / veya /slow endpoint’lerine istek attığınızda, response header’larında X-Process-Time değerini göreceksiniz. Bu her endpoint için ne kadar süre harcandığını gösterir.

Dikkat: Özel header’lar eklerken X- prefix’i kullanmak yaygın bir uygulamadır [1].

Gerçek Dünya Kullanım Senaryoları

Şimdi middleware’in gerçek projelerde nasıl kullanıldığını görelim. Birlikte 4 farklı senaryo üzerinde çalışacağız.

1. Request Logging (İstek Kayıt Sistemi)

Logging, uygulama geliştirmede kritik bir debugging ve monitoring aracıdır [14]. Her gelen isteği kaydetmek için middleware kullanabiliriz [17]:

import logging
from fastapi import FastAPI, Request
from datetime import datetime

# Logger yapılandırması
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI()

@app.middleware("http")
async def log_requests(request: Request, call_next):
    # İstek bilgilerini logla
    logger.info(f"[{datetime.now()}] {request.method} {request.url}")
    logger.info(f"İstemci IP: {request.client.host}")
    
    # İsteği işle
    response = await call_next(request)
    
    # Response durumunu logla
    logger.info(f"Status Code: {response.status_code}")
    
    return response

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id, "name": "Ahmet"}

Bu middleware sayesinde konsolunuzda şöyle loglar göreceksiniz:

INFO: [2025-10-25 10:30:45] GET http://localhost:8000/users/123
INFO: İstemci IP: 127.0.0.1
INFO: Status Code: 200

2. Authentication Middleware (Kimlik Doğrulama)

Kimlik doğrulama işlemleri middleware’in en yaygın kullanım alanlarından biridir [9]. Token kontrolü yapan bir middleware yazalım [17]:

from fastapi import FastAPI, Request, HTTPException
from typing import Callable

app = FastAPI()

# Güvenli endpoint'ler listesi
SECURE_PATHS = ["/admin", "/dashboard", "/api/private"]

@app.middleware("http")
async def auth_middleware(request: Request, call_next: Callable):
    # Path güvenli mi kontrol et
    path = request.url.path
    
    if any(path.startswith(secure_path) for secure_path in SECURE_PATHS):
        # Authorization header var mı?
        auth_header = request.headers.get("Authorization")
        
        if not auth_header:
            raise HTTPException(
                status_code=401,
                detail="Authorization header eksik"
            )
        
        # Token geçerli mi? (basit kontrol)
        if not auth_header.startswith("Bearer "):
            raise HTTPException(
                status_code=401,
                detail="Geçersiz token formatı"
            )
        
        token = auth_header.replace("Bearer ", "")
        
        # Gerçek uygulamada burada token doğrulama yapılır
        if token != "secret-token-123":
            raise HTTPException(
                status_code=403,
                detail="Geçersiz token"
            )
    
    # Her şey yolunda, devam et
    response = await call_next(request)
    return response

@app.get("/")
async def public_endpoint():
    return {"message": "Bu herkese açık"}

@app.get("/admin/users")
async def admin_endpoint():
    return {"message": "Bu sadece admin'lere açık"}

Bu middleware ile /admin altındaki tüm endpoint’ler otomatik olarak korunur.

3. Custom Header Ekleme

Her response’a özel header’lar eklemek, API versiyonu veya server bilgisi gibi metadata paylaşmak için kullanılır [2]:

from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_custom_headers(request: Request, call_next):
    response = await call_next(request)
    
    # Özel header'lar ekle
    response.headers["X-API-Version"] = "1.0.0"
    response.headers["X-Server-Name"] = "FastAPI-Production"
    response.headers["X-Request-ID"] = str(id(request))  # Unique ID
    
    return response

@app.get("/products")
async def get_products():
    return [
        {"id": 1, "name": "Laptop"},
        {"id": 2, "name": "Mouse"}
    ]

Artık her response şu header’ları içerecek:

X-API-Version: 1.0.0
X-Server-Name: FastAPI-Production
X-Request-ID: 140234567890123

4. CORS (Cross-Origin Resource Sharing) Yönetimi

CORS, web uygulamalarında farklı origin’lerden gelen istekleri yönetmek için kullanılır [11]. FastAPI, CORSMiddleware adında yerleşik bir middleware sunar [2]:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# CORS middleware'i ekle
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000", "https://myapp.com"],
    allow_credentials=True,
    allow_methods=["*"],  # Tüm HTTP metodları
    allow_headers=["*"],  # Tüm header'lar
)

@app.get("/api/data")
async def get_data():
    return {"data": "Bu cross-origin isteklere açık"}

Önemli Not: Development ortamında allow_origins=[“*”] kullanabilirsiniz ancak production’da mutlaka belirli origin’leri belirtmelisiniz [2].

5. Rate Limiting (İstek Sınırlama)

Rate limiting, API’nizin kötüye kullanımını önlemek için IP başına istek sayısını sınırlar [14][15]. Basit bir rate limiter yazalım:

from fastapi import FastAPI, Request, HTTPException
from collections import defaultdict
from datetime import datetime, timedelta

app = FastAPI()

# IP başına istek sayacı
request_counts = defaultdict(lambda: {"count": 0, "reset_time": datetime.now()})

# Dakika başına maksimum 10 istek
MAX_REQUESTS = 10
TIME_WINDOW = timedelta(minutes=1)

@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
    client_ip = request.client.host
    current_time = datetime.now()
    
    # IP'nin geçmiş isteklerini kontrol et
    client_data = request_counts[client_ip]
    
    # Zaman penceresi dolmuş mu?
    if current_time > client_data["reset_time"]:
        # Sayacı sıfırla
        client_data["count"] = 0
        client_data["reset_time"] = current_time + TIME_WINDOW
    
    # İstek limitini aştı mı?
    if client_data["count"] >= MAX_REQUESTS:
        raise HTTPException(
            status_code=429,
            detail="Çok fazla istek gönderdiniz. Lütfen bekleyin."
        )
    
    # İstek sayısını artır
    client_data["count"] += 1
    
    # İsteği işle
    response = await call_next(request)
    
    # Response header'larına limit bilgilerini ekle
    response.headers["X-RateLimit-Limit"] = str(MAX_REQUESTS)
    response.headers["X-RateLimit-Remaining"] = str(
        MAX_REQUESTS - client_data["count"]
    )
    
    return response

@app.get("/api/search")
async def search(query: str):
    return {"query": query, "results": []}

Bu middleware, her IP adresinden dakikada maksimum 10 istek kabul eder. Limit aşıldığında 429 (Too Many Requests) hatası döner.

Middleware’i Ne Zaman Kullanmalıyız?

Middleware kullanımı için ideal senaryolar:

✅ Kullan:

  1. Cross-Cutting Concerns (Kesişen Kaygılar): Logging, authentication, metrics gibi tüm endpoint’leri ilgilendiren işlemler [12]
  2. Request/Response Modifikasyonu: Her istekte veya response’da yapılması gereken değişiklikler [6]
  3. Performans Monitoring: İstek sürelerini ölçme, metrics toplama [14][15]
  4. Güvenlik: Rate limiting, IP filtering, token validation [20]
  5. Header Yönetimi: CORS, custom header’lar, security header’ları [11]

❌ Kullanma:

  1. Endpoint-Specific Logic: Sadece belirli bir endpoint’e özel mantık (bunun için route decorator kullan) [12]
  2. Heavy Processing: Middleware’deki ağır işlemler tüm uygulamanızı yavaşlatır [10]
  3. Business Logic: İş mantığı route handler’larda olmalı, middleware’de değil [6]
  4. Dependency Injection Gerektiren Durumlar: FastAPI’nin dependency sistemi daha uygun olabilir [4]

Middleware Çalışma Sırası (Execution Order)

Birden fazla middleware eklerken, son eklenen middleware en dıştadır ve ilk çalışır [1]. Bunu şöyle düşünebiliriz:

app = FastAPI()

@app.middleware("http")
async def first_middleware(request: Request, call_next):
    print("1. Middleware başladı")
    response = await call_next(request)
    print("1. Middleware bitti")
    return response

@app.middleware("http")
async def second_middleware(request: Request, call_next):
    print("2. Middleware başladı")
    response = await call_next(request)
    print("2. Middleware bitti")
    return response

@app.get("/test")
async def test():
    print("Route handler çalışıyor")
    return {"message": "test"}

İstek geldiğinde çıktı:

2. Middleware başladı
1. Middleware başladı
Route handler çalışıyor
1. Middleware bitti
2. Middleware bitti

Son eklenen middleware ilk çalışır (soğan katmanları gibi) [1].

Best Practices (En İyi Uygulamalar)

  1. Async Kullanın: Middleware fonksiyonlarınızı async def olarak tanımlayın [1]
  2. Hata Yönetimi: Middleware’de exception handling yapın [6]
  3. Performans: Middleware’de minimum işlem yapın [10]
  4. Logging: Detaylı loglar tutun ama performansı düşürmeyin [14]
  5. Testing: Middleware’lerinizi mutlaka test edin [6]
  6. Documentation: Middleware’in ne yaptığını dokümante edin [12]
# ✅ İyi örnek
@app.middleware("http")
async def good_middleware(request: Request, call_next):
    try:
        # Hızlı işlemler yap
        start = time.perf_counter()
        response = await call_next(request)
        duration = time.perf_counter() - start
        
        # Sadece gerekli header'ı ekle
        response.headers["X-Time"] = str(duration)
        return response
    except Exception as e:
        logger.error(f"Middleware hatası: {e}")
        raise

# ❌ Kötü örnek
@app.middleware("http")
def bad_middleware(request: Request, call_next):  # async değil!
    # Ağır database sorgusu (middleware'de yapma!)
    result = heavy_database_query()  
    # Senkron çağrı (yavaşlatır!)
    response = call_next(request)
    return response

Middleware vs Dependency Injection

FastAPI’de dependency injection sistemi de var. Peki ne zaman middleware, ne zaman dependency kullanmalıyız [4]?

Middleware kullan:

  • Tüm endpoint’leri etkileyen global işlemler
  • Request/response lifecycle’ını tamamen kontrol etmek

Dependency kullan:

  • Sadece belirli endpoint’lerin ihtiyaç duyduğu işlemler
  • Database connection, user authentication gibi paylaşılan kaynaklar
  • Test edilebilirliği önemli olan yerler

Sonuç ve Özet

Bu yazıda FastAPI middleware konusunu ele aldık! Özetle;

@app.middleware("http") her HTTP isteğinde çalışır
✅ Middleware request-response lifecycle’ında kritik rol oynar
call_next() ile pipeline kontrolü sağlarız
✅ Logging, authentication, rate limiting gibi gerçek dünya problemlerini çözer
✅ Best practices’leri uygulayarak performanslı middleware yazabiliriz

Middleware, FastAPI uygulamalarınızı daha güvenli, izlenebilir ve yönetilebilir hale getirir. Doğru kullanıldığında, middleware uygulamanızın esnekliğini ve yeniden kullanılabilirliğini önemli ölçüde artırır [12].

Bundan sonra neler deneyebilirsiniz?

  1. Kendi FastAPI projenizde basit bir logging middleware yazın
  2. Response time ölçümü yapan middleware’i test edin
  3. Authentication middleware’i kendi güvenlik ihtiyaçlarınıza göre uyarlayın
  4. Middleware stack’i deneyin – birden fazla middleware ekleyin ve sıralamasını gözlemleyin

Kaynaklar

[1] FastAPI Official Documentation – Middleware. https://fastapi.tiangolo.com/tutorial/middleware/

[2] Orchestra – FastAPI Middleware: A Comprehensive Guide. https://www.getorchestra.io/guides/fastapi-middleware-a-comprehensive-guide

[3] TutorialsPoint – FastAPI Middleware. https://www.tutorialspoint.com/fastapi/fastapi_middleware.htm

[4] Stack Overflow – How to write a custom FastAPI middleware class. https://stackoverflow.com/questions/71525132/how-to-write-a-custom-fastapi-middleware-class

[5] Medium – FastAPI: Experiment Middleware feature. https://medium.com/@life-is-short-so-enjoy-it/fastapi-experiment-middleware-feature-c0a0c7314d74

[6] Semaphore – Building Custom Middleware in FastAPI. https://semaphore.io/blog/custom-middleware-fastapi

[7] DEV Community – Creating Middlewares in FastAPI: A Step-by-Step Guide. https://dev.to/aakashkhanna/creating-middlewares-in-fastapi-a-step-by-step-guide-1b4m

[8] Medium – Middleware in FastAPI: From Basic Implementation to Route-Based Strategies. https://medium.com/@saveriomazza/mastering-middleware-in-fastapi-from-basic-implementation-to-route-based-strategies-d62eff6b5463

[9] Orchestra – FastAPI Advanced Middleware Tutorial. https://www.getorchestra.io/guides/fastapi-advanced-middleware-tutorial

[10] Sentry – Write a custom FastAPI middleware class. https://sentry.io/answers/write-a-custom-fastapi-middleware-class/

[11] FastAPI Official Documentation – Advanced Middleware. https://fastapi.tiangolo.com/advanced/middleware/

[12] Semaphore – Custom Middleware Benefits and Use Cases. https://semaphore.io/blog/custom-middleware-fastapi

[13] Medium – Understanding When and How to Implement FastAPI Middleware. https://medium.com/data-science/understanding-when-and-how-to-implement-fastapi-middleware-examples-and-use-cases-c2bd37cb4ffe

[14] Towards Data Science – FastAPI Middleware Examples and Use Cases. https://towardsdatascience.com/understanding-when-and-how-to-implement-fastapi-middleware-examples-and-use-cases-c2bd37cb4ffe/

[15] Mike Huls – FastAPI Middleware Implementation. https://mikehuls.com/fastapi-middleware/

[16] Medium – FastAPI Middleware Experimentation. https://medium.com/@life-is-short-so-enjoy-it/fastapi-experiment-middleware-feature-c0a0c7314d74

[17] Stackademic – Middleware in FastAPI: Request and Response Handling. https://blog.stackademic.com/middleware-in-fastapi-how-to-create-and-use-middleware-to-handle-requests-and-responses-4637b614d1bd

[18] TutorialsPoint – FastAPI Middleware Documentation. https://www.tutorialspoint.com/fastapi/fastapi_middleware.htm

[19] Medium – Mastering Middleware in FastAPI. https://medium.com/@saveriomazza/mastering-middleware-in-fastapi-from-basic-implementation-to-route-based-strategies-d62eff6b5463

[20] Codepunk – Setting Up Middleware in FastAPI. https://www.oneloop.ai/blog/setting-up-middleware-in-fastapi

0

Bir yanıt yazın

Password Requirements:

  • At least 8 characters
  • At least 1 lowercase letter
  • At least 1 uppercase letter
  • At least 1 numerical number
  • At least 1 special character