← Kursa Dön
📄 Text · 20 min

Web Framework Temelleri: Flask ve FastAPI

Python öğrendin, fonksiyonlar yazdın, class'lar tasarladın, dosyalarla çalıştın. Güzel. Ama şu ana kadar yazdığın her program terminalde çalışıyordu. Gerçek dünyada ise kullanıcılar tarayıcıdan, mobil uygulamadan veya başka servislerden senin koduna erişmek istiyor. İşte tam bu noktada web framework'leri sahneye çıkıyor.

Bu derste Python'un en popüler iki web framework'ünü — Flask ve FastAPI — sıfırdan öğreneceksin. İlk web uygulamanı yazacak, REST API tasarlayacak ve iki framework'ü karşılaştırarak hangisini ne zaman kullanman gerektiğini anlayacaksın.


1. Web Framework Nedir?

🏗️ Analoji: İnşaat ve İskele

Bir bina inşa ettiğini düşün. Temelden çatıya kadar her şeyi kendin yapabilirsin — tuğlayı kendin kes, harcı kendin kar, iskeleti kendin dik. Ama bu hem yavaş hem de hata riski yüksek. İnşaat firmaları bunun yerine hazır iskele sistemleri kullanır: kurulumu hızlı, güvenli ve standart.

Web framework de aynen bu iskele gibi. HTTP isteklerini dinleme, URL yönlendirme (routing), güvenlik, hata yönetimi gibi tekrar eden işleri senin için halledip, sen sadece iş mantığına (business logic) odaklanırsın.

Neden Framework Kullanmalıyız?

Framework olmadan bir web sunucusu yazmak için socket modülüyle TCP bağlantısı açıp, HTTP protokolünü elle parse etmen gerekir. Bu yüzlerce satır kod demek — ve güvenlik açıkları, edge case'ler cabası.

Framework'ler şunları hazır sunar:

  • Routing: URL'leri fonksiyonlara bağlama (/usersget_users())

  • Request parsing: Gelen veriyi (JSON, form data, query params) otomatik çözümleme

  • Response formatting: JSON, HTML, dosya gibi yanıtları düzgün formatta gönderme

  • Middleware: İstek-yanıt döngüsüne araya girme (loglama, auth kontrolü)

  • Hata yönetimi: 404, 500 gibi hataları düzgün şekilde ele alma


2. Flask: Mikro Framework

Flask, Python dünyasının en bilinen web framework'üdür. "Mikro" denmesinin sebebi, çekirdeğinin küçük ve sade olması. İhtiyacın olan her şeyi eklentilerle (extension) eklersin. Bu felsefesi sayesinde öğrenmesi kolay, esnekliği yüksek.

Kurulum

Flask, pip ile kurulur. Bir sanal ortam (virtual environment) oluşturup içine kurmak en iyi pratiktir:

# Sanal ortam oluştur ve aktifle
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# Flask kur
pip install flask

İlk Flask Uygulaması: Hello World

from flask import Flask

# Flask uygulaması oluştur
app = Flask(__name__)

# Ana sayfa route'u
@app.route("/")
def hello():
    return "Merhaba, Dünya!"

# Uygulamayı çalıştır
if __name__ == "__main__":
    app.run(debug=True, port=5000)

Bu dosyayı app.py olarak kaydet ve terminalde python app.py ile çalıştır. Tarayıcıda http://localhost:5000 adresine git — "Merhaba, Dünya!" yazısını göreceksin.

Burada neler oluyor:

  • Flask(__name__) → Uygulama nesnesi oluşturuyoruz. __name__ Flask'a modülün konumunu söyler.

  • @app.route("/") → Bu decorator, "/" URL'sine gelen istekleri hello() fonksiyonuna yönlendirir.

  • app.run(debug=True) → Geliştirme sunucusunu başlatır. debug=True kodu değiştirince otomatik yeniden başlatır.

⚠️ Dikkat: debug=True sadece geliştirme ortamında kullanılır. Production'da asla açık bırakma — hata mesajları hassas bilgileri sızdırabilir.


3. Route Tanımlama ve HTTP Metodları

Web'de her şey URL'ler ve HTTP metodları üzerinden çalışır. Flask'ta route'lar decorator'larla tanımlanır.

Temel Route'lar

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Ana Sayfa"

@app.route("/hakkimizda")
def about():
    return "Hakkımızda Sayfası"

@app.route("/iletisim")
def contact():
    return "İletişim Sayfası"

if __name__ == "__main__":
    app.run(debug=True)

Her @app.route() bir URL pattern'ını bir fonksiyona bağlar. Tarayıcıdan /hakkimizda adresine gidersen about() fonksiyonu çalışır.

Dinamik Route'lar (Path Parameters)

URL'nin bir parçasını değişken olarak alabilirsin:

from flask import Flask

app = Flask(__name__)

@app.route("/kullanici/<isim>")
def profil(isim):
    return f"Hoş geldin, {isim}!"

@app.route("/urun/<int:urun_id>")
def urun_detay(urun_id):
    return f"Ürün ID: {urun_id}"

@app.route("/dosya/<path:dosya_yolu>")
def dosya_goster(dosya_yolu):
    return f"Dosya: {dosya_yolu}"

if __name__ == "__main__":
    app.run(debug=True)

<isim> kısmı URL'den gelen değeri isim parametresine atar. <int:urun_id> ise otomatik olarak integer'a dönüştürür — eğer dönüştürülemezse 404 hatası döner.

Desteklenen tip dönüştürücüler:

DönüştürücüAçıklamaÖrnek
stringVarsayılan, slash hariç her şey<isim>
intPozitif tam sayı<int:id>
floatOndalık sayı<float:fiyat>
pathSlash dahil string<path:dosya>

HTTP Metodları

Web'de dört temel işlem vardır (CRUD): Create, Read, Update, Delete. Her biri bir HTTP metoduna karşılık gelir:

HTTP MetoduCRUD İşlemiÖrnek Kullanım
GETReadKullanıcı listesini getir
POSTCreateYeni kullanıcı oluştur
PUTUpdateKullanıcı bilgilerini güncelle
DELETEDeleteKullanıcıyı sil

Flask'ta metod belirtmek için methods parametresini kullanırsın:

from flask import Flask, request, jsonify

app = Flask(__name__)

# Basit in-memory veri
users = [
    {"id": 1, "name": "Ahmet", "email": "ahmet@test.com"},
    {"id": 2, "name": "Ayşe", "email": "ayse@test.com"},
]

@app.route("/users", methods=["GET"])
def get_users():
    return jsonify(users)

@app.route("/users", methods=["POST"])
def create_user():
    data = request.get_json()
    new_user = {
        "id": len(users) + 1,
        "name": data["name"],
        "email": data["email"]
    }
    users.append(new_user)
    return jsonify(new_user), 201

@app.route("/users/<int:user_id>", methods=["PUT"])
def update_user(user_id):
    data = request.get_json()
    for user in users:
        if user["id"] == user_id:
            user["name"] = data.get("name", user["name"])
            user["email"] = data.get("email", user["email"])
            return jsonify(user)
    return jsonify({"error": "Kullanıcı bulunamadı"}), 404

@app.route("/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
    for i, user in enumerate(users):
        if user["id"] == user_id:
            users.pop(i)
            return jsonify({"message": "Silindi"}), 200
    return jsonify({"error": "Kullanıcı bulunamadı"}), 404

if __name__ == "__main__":
    app.run(debug=True)

Bu örnek basit bir REST API. request.get_json() gelen JSON body'yi parse eder, jsonify() ise Python dict'ini JSON response'a çevirir. 201 status kodu "Created" anlamına gelir.


4. Request ve Response İşleme

Flask'ta gelen isteğin her detayına request nesnesiyle ulaşırsın.

Request Nesnesi

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/ara")
def search():
    # Query parameter: /ara?q=python&sayfa=2
    query = request.args.get("q", "")
    page = request.args.get("sayfa", 1, type=int)
    return jsonify({
        "arama": query,
        "sayfa": page
    })

@app.route("/giris", methods=["POST"])
def login():
    # JSON body
    if request.is_json:
        data = request.get_json()
        username = data.get("username")
        password = data.get("password")
        return jsonify({"message": f"Hoş geldin, {username}!"})
    
    # Form data
    username = request.form.get("username")
    password = request.form.get("password")
    return jsonify({"message": f"Hoş geldin, {username}!"})

@app.route("/bilgi")
def info():
    return jsonify({
        "method": request.method,
        "url": request.url,
        "headers": dict(request.headers),
        "remote_addr": request.remote_addr
    })

if __name__ == "__main__":
    app.run(debug=True)

request.args query parameter'larına (URL'deki ?key=value kısımları), request.form ise form verilerine erişir. request.get_json() JSON body'yi Python dict'ine çevirir.

Response Döndürme

Flask'ta birkaç farklı şekilde response döndürebilirsin:

from flask import Flask, jsonify, make_response, redirect, url_for

app = Flask(__name__)

# 1. Düz string — otomatik 200 OK
@app.route("/text")
def text_response():
    return "Düz metin yanıt"

# 2. JSON response
@app.route("/json")
def json_response():
    return jsonify({"status": "ok", "data": [1, 2, 3]})

# 3. Tuple ile status code ve header
@app.route("/olusturuldu")
def created():
    return jsonify({"id": 42}), 201, {"X-Custom": "Header"}

# 4. make_response ile detaylı kontrol
@app.route("/ozel")
def custom_response():
    response = make_response(jsonify({"msg": "Özel yanıt"}))
    response.status_code = 200
    response.headers["X-App-Version"] = "1.0"
    response.set_cookie("tema", "dark", max_age=3600)
    return response

# 5. Yönlendirme
@app.route("/eski-sayfa")
def old_page():
    return redirect(url_for("json_response"))

if __name__ == "__main__":
    app.run(debug=True)

jsonify() otomatik olarak Content-Type: application/json header'ını ekler. Tuple döndürürken sıra (body, status_code, headers) şeklindedir.


5. Jinja2 Templating (Kısaca)

Flask sadece API değil, HTML sayfaları da sunabilir. Bunun için Jinja2 template engine'ini kullanır. Jinja2, HTML dosyalarının içine Python benzeri mantık eklemenizi sağlar.

Proje Yapısı

proje/
├── app.py
├── templates/
│   ├── base.html
│   └── index.html
└── static/
    └── style.css

Flask, template'leri templates/ klasöründe arar.

Basit Template Örneği

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html", 
                           baslik="Ana Sayfa",
                           isim="Ahmet",
                           beceriler=["Python", "Flask", "SQL"])

if __name__ == "__main__":
    app.run(debug=True)

templates/index.html dosyası:

<!DOCTYPE html>
<html>
<head>
    <title>{{ baslik }}</title>
</head>
<body>
    <h1>Merhaba, {{ isim }}!</h1>
    
    <h2>Beceriler:</h2>
    <ul>
        {% for beceri in beceriler %}
            <li>{{ beceri }}</li>
        {% endfor %}
    </ul>
    
    {% if beceriler|length > 2 %}
        <p>Çok yeteneklisin! 🎉</p>
    {% endif %}
</body>
</html>

Jinja2'nin temel sözdizimi:

  • {{ degisken }} → Değişkeni yazdırır

  • {% for ... %} → Döngü

  • {% if ... %} → Koşul

  • {# yorum #} → Template yorumu

Template inheritance (kalıtım) ile base.html oluşturup diğer sayfaların onu extend etmesini sağlayabilirsin — tam bir web sitesi iskeleti kurabilirsin.

💡 İpucu: Sadece REST API geliştiriyorsan Jinja2'ye ihtiyacın yok. Ama admin paneli, dashboard gibi basit arayüzler için oldukça pratik.


6. Hata Yönetimi

Flask'ta özel hata sayfaları ve hata handler'ları tanımlayabilirsin:

from flask import Flask, jsonify

app = Flask(__name__)

# Özel 404 handler
@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Sayfa bulunamadı", "code": 404}), 404

# Özel 500 handler
@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "Sunucu hatası", "code": 500}), 500

# Manuel hata fırlatma
@app.route("/kullanici/<int:uid>")
def get_user(uid):
    if uid > 100:
        from flask import abort
        abort(404)  # 404 handler'ına yönlendir
    return jsonify({"id": uid, "name": "Test"})

if __name__ == "__main__":
    app.run(debug=True)

abort() fonksiyonu belirtilen HTTP hata kodunu tetikler ve ilgili errorhandler'a yönlendirir. API geliştirirken tutarlı hata yanıtları döndürmek çok önemli.


7. FastAPI: Modern ve Hızlı

FastAPI, Python'un daha yeni nesil web framework'üdür. 2018'de Sebastián Ramírez tarafından yaratılmış ve kısa sürede muazzam bir popülerlik kazanmıştır. Flask'tan farklı olarak type hint'leri aktif olarak kullanır ve bunlardan otomatik dokümantasyon üretir.

Neden FastAPI?

  • Performans: ASGI tabanlı, async/await desteği sayesinde Node.js ve Go ile yarışır.

  • Otomatik dokümantasyon: Type hint'lerden Swagger UI ve ReDoc otomatik oluşur.

  • Veri doğrulama: Pydantic ile gelen veriler otomatik doğrulanır (validation).

  • Modern Python: Type hints, async/await, dataclass — Python 3.6+ özellikleri tam kullanılır.

  • Editor desteği: Type hint'ler sayesinde IDE'ler mükemmel autocompletion sunar.

Kurulum

pip install fastapi uvicorn

uvicorn, FastAPI uygulamalarını çalıştıran ASGI sunucusudur.

İlk FastAPI Uygulaması

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello():
    return {"message": "Merhaba, Dünya!"}

@app.get("/items/{item_id}")
def get_item(item_id: int, q: str = None):
    return {"item_id": item_id, "query": q}

Çalıştırmak için:

uvicorn app:app --reload --port 8000

Tarayıcıda http://localhost:8000 adresine git. Ama asıl sürpriz http://localhost:8000/docs adresinde — otomatik oluşan Swagger UI dokümantasyonunu göreceksin! Her endpoint'i buradan test edebilirsin.

Dikkat ettiysen Flask'taki @app.route("/", methods=["GET"]) yerine doğrudan @app.get("/") kullanıyoruz. Daha temiz, daha okunabilir. item_id: int type hint'i sayesinde FastAPI otomatik olarak path parametresini integer'a dönüştürür ve geçersiz değerlerde detaylı hata mesajı döndürür.


8. Path Parameters, Query Parameters ve Request Body

FastAPI'nin en güçlü yanlarından biri, type hint'lere bakarak parametrelerin nereden geldiğini otomatik anlamasıdır.

Path Parameters

from fastapi import FastAPI

app = FastAPI()

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

# Sabit path'ler dinamik olanlardan ÖNCE tanımlanmalı
@app.get("/users/me")
def get_current_user():
    return {"user": "Ben"}

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

⚠️ Dikkat: Route sırası önemlidir! /users/me path'ini /users/{user_id} path'inden önce tanımlamalısın. Aksi halde FastAPI "me" string'ini integer'a çevirmeye çalışır ve hata alırsın.

Query Parameters

Fonksiyondaki path'te olmayan parametreler otomatik olarak query parameter kabul edilir:

from fastapi import FastAPI
from typing import Optional

app = FastAPI()

@app.get("/items")
def list_items(skip: int = 0, limit: int = 10, search: Optional[str] = None):
    result = {"skip": skip, "limit": limit}
    if search:
        result["search"] = search
    return result

# Çağrı: /items?skip=5&limit=20&search=python

skip ve limit varsayılan değere sahip — opsiyonel. search ise Optional[str] ile tanımlanmış — None olabilir. FastAPI tüm dönüşüm ve doğrulamayı otomatik yapar.

Request Body: Pydantic Modelleri

POST veya PUT isteklerinde gelen JSON body'yi doğrulamak için Pydantic modellerini kullanırsın:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, Field
from typing import Optional

app = FastAPI()

# Pydantic modeli — veri şeması
class UserCreate(BaseModel):
    name: str = Field(..., min_length=2, max_length=50)
    email: str
    age: Optional[int] = Field(None, ge=0, le=150)

class UserResponse(BaseModel):
    id: int
    name: str
    email: str
    age: Optional[int] = None

users_db = []

@app.post("/users", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
    new_user = {"id": len(users_db) + 1, **user.model_dump()}
    users_db.append(new_user)
    return new_user

Pydantic modeli sayesinde:

  • Gelen JSON otomatik doğrulanır (validation)

  • name en az 2, en fazla 50 karakter olmalı

  • age 0-150 arasında olmalı (verilmezse None)

  • Yanlış tipte veri gönderilirse detaylı hata mesajı döner

  • response_model ile yanıt formatı da kontrol edilir

Bu doğrulama Flask'ta kendin yazman gereken onlarca satır kodu tek bir model tanımıyla halleder.


9. FastAPI ile Todo API Projesi

Şimdi öğrendiklerimizi birleştirip gerçek bir REST API yazalım:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime

app = FastAPI(
    title="Todo API",
    description="Basit bir görev yönetim API'si",
    version="1.0.0"
)

# --- Modeller ---
class TodoCreate(BaseModel):
    title: str = Field(..., min_length=1, max_length=200)
    description: Optional[str] = None
    completed: bool = False

class TodoUpdate(BaseModel):
    title: Optional[str] = Field(None, min_length=1, max_length=200)
    description: Optional[str] = None
    completed: Optional[bool] = None

class TodoResponse(BaseModel):
    id: int
    title: str
    description: Optional[str]
    completed: bool
    created_at: str

# --- In-memory veritabanı ---
todos: list[dict] = []
next_id = 1

# --- Endpoint'ler ---
@app.get("/todos", response_model=list[TodoResponse])
def list_todos(completed: Optional[bool] = None):
    """Tüm görevleri listele. completed ile filtreleyebilirsin."""
    if completed is not None:
        return [t for t in todos if t["completed"] == completed]
    return todos

@app.get("/todos/{todo_id}", response_model=TodoResponse)
def get_todo(todo_id: int):
    """Belirli bir görevi getir."""
    for todo in todos:
        if todo["id"] == todo_id:
            return todo
    raise HTTPException(status_code=404, detail="Görev bulunamadı")

@app.post("/todos", response_model=TodoResponse, status_code=201)
def create_todo(todo: TodoCreate):
    """Yeni görev oluştur."""
    global next_id
    new_todo = {
        "id": next_id,
        "title": todo.title,
        "description": todo.description,
        "completed": todo.completed,
        "created_at": datetime.now().isoformat()
    }
    todos.append(new_todo)
    next_id += 1
    return new_todo

@app.put("/todos/{todo_id}", response_model=TodoResponse)
def update_todo(todo_id: int, todo: TodoUpdate):
    """Mevcut görevi güncelle."""
    for existing in todos:
        if existing["id"] == todo_id:
            if todo.title is not None:
                existing["title"] = todo.title
            if todo.description is not None:
                existing["description"] = todo.description
            if todo.completed is not None:
                existing["completed"] = todo.completed
            return existing
    raise HTTPException(status_code=404, detail="Görev bulunamadı")

@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):
    """Görevi sil."""
    for i, todo in enumerate(todos):
        if todo["id"] == todo_id:
            todos.pop(i)
            return {"message": "Görev silindi"}
    raise HTTPException(status_code=404, detail="Görev bulunamadı")

Bu API'yi uvicorn app:app --reload ile çalıştırdığında:

  • GET /todos → Tüm görevleri listeler

  • GET /todos?completed=true → Sadece tamamlanmışları getirir

  • GET /todos/1 → ID'si 1 olan görevi getirir

  • POST /todos → Yeni görev oluşturur (JSON body gerekir)

  • PUT /todos/1 → Görevi günceller

  • DELETE /todos/1 → Görevi siler

  • GET /docs → Otomatik Swagger dokümantasyonu

HTTPException ile hata fırlattığında FastAPI bunu düzgün bir JSON hata yanıtına dönüştürür: {"detail": "Görev bulunamadı"}.


10. FastAPI'de Async Desteği

FastAPI'nin en büyük avantajlarından biri async/await desteğidir. Veritabanı sorgusu, API çağrısı gibi I/O yoğun işlemlerde performansı dramatik şekilde artırır:

from fastapi import FastAPI
import asyncio

app = FastAPI()

# Senkron endpoint — basit işlemler için yeterli
@app.get("/sync")
def sync_endpoint():
    return {"type": "sync"}

# Asenkron endpoint — I/O yoğun işlemler için
@app.get("/async")
async def async_endpoint():
    await asyncio.sleep(1)  # Simüle: DB sorgusu, API çağrısı
    return {"type": "async"}

Flask'ta async desteği sonradan ve sınırlı olarak eklenmiştir. FastAPI ise en başından async-native tasarlanmıştır.

💡 İpucu: Eğer fonksiyonunda await kullanmıyorsan def yeterli — FastAPI senkron fonksiyonları bir thread pool'da çalıştırır. async def kullanman için gerçekten bir await çağrısı olmalı.


11. Middleware ve Dependency Injection

Middleware

Middleware, her istek-yanıt döngüsünde çalışan ara katmandır. Loglama, CORS, authentication gibi çapraz kesim (cross-cutting) konuları için kullanılır.

Flask'ta middleware:

from flask import Flask, request, g
import time

app = Flask(__name__)

@app.before_request
def before():
    g.start_time = time.time()

@app.after_request
def after(response):
    duration = time.time() - g.start_time
    response.headers["X-Response-Time"] = f"{duration:.4f}s"
    return response

@app.route("/")
def index():
    return "OK"

if __name__ == "__main__":
    app.run(debug=True)

FastAPI'de middleware:

from fastapi import FastAPI, Request
import time

app = FastAPI()

@app.middleware("http")
async def add_timing(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    duration = time.time() - start
    response.headers["X-Response-Time"] = f"{duration:.4f}s"
    return response

@app.get("/")
def index():
    return {"message": "OK"}

FastAPI Dependency Injection

FastAPI'nin Depends mekanizması, tekrar eden mantığı (authentication, DB bağlantısı) zarif şekilde paylaştırır:

from fastapi import FastAPI, Depends, HTTPException, Header

app = FastAPI()

# Bağımlılık fonksiyonu — her istekte çalışır
def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key != "secret-key-123":
        raise HTTPException(status_code=401, detail="Geçersiz API key")
    return x_api_key

@app.get("/protected")
def protected_route(api_key: str = Depends(verify_api_key)):
    return {"message": "Gizli veriye eriştin!", "key": api_key}

@app.get("/also-protected")
def another_protected(api_key: str = Depends(verify_api_key)):
    return {"message": "Burası da korumalı!"}

Depends(verify_api_key) sayesinde verify_api_key fonksiyonu endpoint çağrılmadan önce çalışır. API key geçersizse 401 hatası döner, istek endpoint'e ulaşmaz. Bu pattern'ı authentication, pagination, database session gibi her yerde kullanabilirsin.


12. Flask vs FastAPI Karşılaştırması

İki framework'ü karşılaştırmak, her birinin ne zaman tercih edilmesi gerektiğini anlamaya yardımcı olur:

ÖzellikFlaskFastAPI
Çıkış yılı20102018
WSGI/ASGIWSGIASGI
Async desteğiSınırlıNative
Veri doğrulamaManuel / WTFormsPydantic (otomatik)
Otomatik dokümantasyonYok (eklentiyle)Swagger + ReDoc (yerleşik)
PerformansOrtaYüksek
Öğrenme eğrisiDüşükOrta-düşük
EkosistemÇok geniş, olgunHızla büyüyen
Type hintsOpsiyonelZorunlu ve aktif
ToplulukÇok büyükBüyük ve aktif

Ne Zaman Flask?

  • Küçük projeler, prototipler, öğrenme amaçlı

  • Server-side rendered HTML sayfaları (Jinja2)

  • Mevcut Flask eklenti ekosistemine ihtiyaç varsa

  • Ekipte Flask deneyimi varsa

Ne Zaman FastAPI?

  • REST API ve mikroservisler

  • Performansın önemli olduğu projeler

  • Otomatik dokümantasyon gerekiyorsa

  • Async I/O yoğun uygulamalar (WebSocket, real-time)

  • Yeni projeler — modern Python yaklaşımı

Aynı Endpoint, İki Framework

Flask versiyonu:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/users", methods=["POST"])
def create_user():
    data = request.get_json()
    if not data or "name" not in data:
        return jsonify({"error": "name gerekli"}), 400
    if not isinstance(data["name"], str):
        return jsonify({"error": "name string olmalı"}), 400
    if len(data["name"]) < 2:
        return jsonify({"error": "name en az 2 karakter"}), 400
    # ... daha fazla doğrulama
    return jsonify({"id": 1, "name": data["name"]}), 201

FastAPI versiyonu:

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class UserCreate(BaseModel):
    name: str = Field(..., min_length=2)

@app.post("/users", status_code=201)
def create_user(user: UserCreate):
    return {"id": 1, "name": user.name}

Fark açık. Flask'ta manuel olarak yaptığın tüm doğrulamayı FastAPI + Pydantic tek satırda hallediyor. Üstelik hata mesajları da otomatik olarak detaylı ve tutarlı oluşuyor.


13. Test Etme: curl ve httpie

API'ni test etmek için curl veya httpie kullanabilirsin:

curl ile test

# GET isteği
curl http://localhost:8000/todos

# POST isteği (JSON body)
curl -X POST http://localhost:8000/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Python öğren", "description": "Flask ve FastAPI"}'

# PUT isteği
curl -X PUT http://localhost:8000/todos/1 \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'

# DELETE isteği
curl -X DELETE http://localhost:8000/todos/1

httpie ile test (daha okunabilir)

# Kurulum: pip install httpie

# GET
http GET localhost:8000/todos

# POST
http POST localhost:8000/todos title="Python öğren" description="Flask ve FastAPI"

# PUT
http PUT localhost:8000/todos/1 completed:=true

# DELETE
http DELETE localhost:8000/todos/1

httpie daha insan dostu bir arayüz sunar — JSON formatını otomatik algılar, renklendirme yapar ve syntax'ı daha kısa tutar.


14. Production Dağıtımı Hakkında

Geliştirme sunucuları (flask run veya uvicorn --reload) production için uygun değildir. Gerçek dağıtımda:

Flask için:

# Gunicorn ile (Linux/Mac)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 app:app

FastAPI için:

# Uvicorn + worker'lar
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4

# Veya Gunicorn + Uvicorn worker
gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000

-w 4 parametresi 4 worker process başlatır — CPU çekirdek sayısına göre ayarlanır (genellikle 2 * CPU + 1 formülü önerilir).


15. Yaygın Hatalar ve İpuçları

1. Circular Import

# ❌ Yanlış — app.py ve routes.py birbirini import ederse
# app.py
from routes import user_routes  # routes.py, app'i import ediyorsa döngü!

# ✅ Doğru — Blueprint kullan (Flask) veya APIRouter kullan (FastAPI)
# Flask
from flask import Blueprint
user_bp = Blueprint("users", __name__)

@user_bp.route("/users")
def get_users():
    return "users"

# app.py'de:
# app.register_blueprint(user_bp)
# FastAPI — APIRouter
from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
def get_users():
    return {"users": []}

# app.py'de:
# app.include_router(router)

2. Global Mutable State

# ❌ Tehlikeli — production'da birden fazla worker olunca
# her worker kendi kopyasına sahip olur
users = []  # Bu sadece demo/prototip için!

# ✅ Doğru — gerçek veritabanı kullan
# SQLite, PostgreSQL, Redis vb.

3. Debug Modunu Production'da Açık Bırakmak

# ❌ Production'da kesinlikle yapma
app.run(debug=True)  # Kaynak kodu görünür, komut çalıştırılabilir!

# ✅ Ortam değişkeni ile kontrol et
import os
debug_mode = os.environ.get("FLASK_DEBUG", "0") == "1"
app.run(debug=debug_mode)

Özet

  • Flask mikro bir framework'tür — basit, esnek, geniş ekosistem. Küçük projeler ve HTML sayfaları için mükemmel.

  • FastAPI modern bir framework'tür — hızlı, type-safe, otomatik dokümantasyon. REST API ve mikroservisler için ideal.

  • Route'lar URL'leri fonksiyonlara bağlar. Path parameter, query parameter ve request body ile veri alınır.

  • Pydantic modelleri FastAPI'de veri doğrulamayı otomatikleştirir — manuel if kontrolleri yerine model tanımla.

  • Middleware her istekte çalışan ara katmandır — loglama, auth, CORS gibi ortak işlemler için kullanılır.

  • Flask'ta @app.route() ve request nesnesi, FastAPI'de @app.get() ve type hint'ler temel yapı taşlarıdır.