Перейти к содержимому

14. FastAPI: основы

Иллюстрация к уроку

FastAPI — один из самых быстрых Python фреймворков. Сравнение с конкурентами:

ФреймворкПроизводительностьDXТипизация
FastAPI⚡⚡⚡ (равен Go/Node)⭐⭐⭐Нативная
Flask⚡⚡⭐⭐Нет
Django⭐⭐⭐Частичная

Плюсы FastAPI:

  • Автоматическая документация (Swagger UI из коробки)
  • Валидация через Pydantic — пишешь типы, получаешь валидацию
  • Async нативно — полная поддержка async/await
  • Зависимости (DI) — мощная система внедрения зависимостей
  • OpenAPI — автогенерация схемы
Окно терминала
pip install fastapi uvicorn
# Или с uv (быстрее):
uv add fastapi uvicorn
main.py
from fastapi import FastAPI
app = FastAPI(
title="My API",
description="Описание API",
version="1.0.0"
)
@app.get("/")
def root():
return {"message": "Hello, World!"}
Окно терминала
# Запуск с hot-reload
uvicorn main:app --reload
# Продакшн
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

Открой: http://localhost:8000/docs — интерактивная документация!

from fastapi import FastAPI
app = FastAPI()
# GET
@app.get("/users")
def get_users():
return [{"id": 1, "name": "Яша"}]
# POST
@app.post("/users")
def create_user(user: dict):
return user
# PUT
@app.put("/users/{user_id}")
def update_user(user_id: int, user: dict):
return {"id": user_id, **user}
# PATCH
@app.patch("/users/{user_id}")
def partial_update(user_id: int, data: dict):
return {"id": user_id, "updated": data}
# DELETE
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
return {"deleted": user_id}
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
# Path параметры
@app.get("/users/{user_id}")
def get_user(user_id: int): # автоматически конвертируется в int!
return {"id": user_id}
@app.get("/items/{item_id}/reviews/{review_id}")
def get_review(item_id: int, review_id: int):
return {"item_id": item_id, "review_id": review_id}
# Query параметры (?page=1&size=10&search=python)
@app.get("/items")
def get_items(
page: int = 1,
size: int = 10,
search: Optional[str] = None, # опциональный
category: str | None = None, # Python 3.10+
):
return {
"page": page,
"size": size,
"search": search,
"category": category
}
# URL: /items?page=2&size=20&search=python&category=books
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
from typing import Optional
app = FastAPI()
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=50, description="Имя пользователя")
email: EmailStr
age: int = Field(ge=0, le=150)
role: str = "user"
class UserResponse(BaseModel):
id: int
name: str
email: str
role: str
created_at: datetime
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=50)
age: Optional[int] = Field(None, ge=0, le=150)
# Используем модели
@app.post("/users", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
# Валидация происходит автоматически!
# Если данные неверные — FastAPI вернёт 422 с описанием ошибок
return {
"id": 1,
"name": user.name,
"email": user.email,
"role": user.role,
"created_at": datetime.now()
}
@app.patch("/users/{user_id}", response_model=UserResponse)
def update_user(user_id: int, update: UserUpdate):
# Обновляем только переданные поля
return {
"id": user_id,
"name": update.name or "Яша",
"email": "[email protected]",
"role": "user",
"created_at": datetime.now()
}
from fastapi import Header, Cookie, Response
from typing import Annotated
@app.get("/profile")
def get_profile(
authorization: Annotated[str | None, Header()] = None,
session_id: Annotated[str | None, Cookie()] = None,
):
return {
"auth": authorization,
"session": session_id
}
@app.post("/login")
def login(response: Response, username: str, password: str):
# Устанавливаем куку
response.set_cookie(key="session_id", value="abc123", httponly=True)
return {"message": "Logged in"}
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: dict):
return item
@app.get("/items/{item_id}")
def get_item(item_id: int):
items = {1: "Apple", 2: "Banana"}
if item_id not in items:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Item {item_id} not found"
)
return {"id": item_id, "name": items[item_id]}
# Кастомные заголовки в исключении
@app.get("/secure")
def secure_endpoint(token: str):
if token != "secret":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"}
)
return {"data": "secret data"}
app/
├── main.py # точка входа
├── core/
│ ├── config.py # настройки
│ └── database.py # подключение к БД
├── models/
│ └── user.py # Pydantic и DB модели
├── routers/
│ ├── users.py # /users эндпоинты
│ └── products.py # /products эндпоинты
├── services/
│ └── user_service.py # бизнес-логика
└── dependencies.py # FastAPI dependencies
routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
def get_users():
return []
@router.get("/{user_id}")
def get_user(user_id: int):
return {"id": user_id}
# main.py
from fastapi import FastAPI
from routers import users, products
app = FastAPI()
app.include_router(users.router)
app.include_router(products.router)
# Создай простое TODO API:
#
# Модели:
# - TodoCreate: title (str), description (str | None), priority (int = 1)
# - TodoUpdate: title (str | None), description (str | None), done (bool | None)
# - TodoResponse: id, title, description, priority, done, created_at
#
# Endpoints:
# GET /todos - список (query: page, size, done: bool | None)
# POST /todos - создать
# GET /todos/{id} - получить
# PATCH /todos/{id} - обновить
# DELETE /todos/{id} - удалить
# POST /todos/{id}/done - отметить выполненным
#
# Хранить данные в памяти (список/словарь)

В следующем уроке — FastAPI CRUD с базой данных!