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 (
/users→get_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 isteklerihello()fonksiyonuna yönlendirir.app.run(debug=True)→ Geliştirme sunucusunu başlatır.debug=Truekodu değiştirince otomatik yeniden başlatır.
⚠️ Dikkat:
debug=Truesadece 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 |
|---|---|---|
string | Varsayılan, slash hariç her şey | <isim> |
int | Pozitif tam sayı | <int:id> |
float | Ondalık sayı | <float:fiyat> |
path | Slash 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 Metodu | CRUD İşlemi | Örnek Kullanım |
|---|---|---|
| GET | Read | Kullanıcı listesini getir |
| POST | Create | Yeni kullanıcı oluştur |
| PUT | Update | Kullanıcı bilgilerini güncelle |
| DELETE | Delete | Kullanı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.cssFlask, 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 uvicornuvicorn, 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 8000Tarayı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/mepath'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=pythonskip 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_userPydantic modeli sayesinde:
Gelen JSON otomatik doğrulanır (validation)
nameen az 2, en fazla 50 karakter olmalıage0-150 arasında olmalı (verilmezseNone)Yanlış tipte veri gönderilirse detaylı hata mesajı döner
response_modelile 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 listelerGET /todos?completed=true→ Sadece tamamlanmışları getirirGET /todos/1→ ID'si 1 olan görevi getirirPOST /todos→ Yeni görev oluşturur (JSON body gerekir)PUT /todos/1→ Görevi güncellerDELETE /todos/1→ Görevi silerGET /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
awaitkullanmıyorsandefyeterli — FastAPI senkron fonksiyonları bir thread pool'da çalıştırır.async defkullanman için gerçekten birawaitç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:
| Özellik | Flask | FastAPI |
|---|---|---|
| Çıkış yılı | 2010 | 2018 |
| WSGI/ASGI | WSGI | ASGI |
| Async desteği | Sınırlı | Native |
| Veri doğrulama | Manuel / WTForms | Pydantic (otomatik) |
| Otomatik dokümantasyon | Yok (eklentiyle) | Swagger + ReDoc (yerleşik) |
| Performans | Orta | Yüksek |
| Öğrenme eğrisi | Düşük | Orta-düşük |
| Ekosistem | Çok geniş, olgun | Hızla büyüyen |
| Type hints | Opsiyonel | Zorunlu ve aktif |
| Topluluk | Çok büyük | Bü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"]}), 201FastAPI 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/1httpie 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/1httpie 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:appFastAPI 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
ifkontrolleri 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()verequestnesnesi, FastAPI'de@app.get()ve type hint'ler temel yapı taşlarıdır.
AI Asistan
Sorularını yanıtlamaya hazır