Создание веб-сервисов с FastAPI: от основ до сложных решений

Раздел: Веб-разработка -> Разработка бэкенда

FastAPI представляет собой современный веб-фреймворк для языка Python, ориентированный на создание высокопроизводительных API. Он сочетает в себе простоту разработки, асинхронную обработку запросов и автоматическую генерацию документации OpenAPI. В данной статье рассматриваются ключевые аспекты разработки бэкенда с использованием FastAPI: установка, маршрутизация, валидация данных, работа с базами данных, аутентификация, тестирование и развертывание.

Основные возможности FastAPI

Как создать минимальное рабочее приложение на FastAPI?

Стандартный подход предполагает использование асинхронных функций (async def) для обработчиков маршрутов, что обеспечивает максимальную производительность при большом количестве одновременных подключений. Установка выполняется через pip:

pip install fastapi uvicorn

Fastapi python backend (разработка бэкенда на fastapi)

Пример основного файла приложения (main.py):

from fastapi import FastAPI

app = FastAPI()

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

Запуск сервера производится командой:

uvicorn main:app --reload

После запуска API становится доступным по адресу http://127.0.0.1:8000. Автоматически генерируется интерактивная документация по адресу /docs.

Типичные ошибки и их устранение:

  • Ошибка импорта FastAPI - проверена ли установка пакета fastapi.
  • Ошибка запуска uvicorn - требуется установка uvicorn отдельно или в составе веб-сервера.
  • Маршрут не отвечает - следует убедиться, что файл main.py находится в текущем каталоге и запуск осуществляется из него.

Как обрабатывать синхронные запросы без потери производительности?

В некоторых случаях (простые вычисления, обращение к синхронным библиотекам) допустимо использовать обычные функции (def). FastAPI автоматически запускает их в отдельном потоке через ThreadPoolExecutor, что не блокирует главный цикл событий. Пример:

from fastapi import FastAPI

app = FastAPI()

@app.get('/sync')
def sync_endpoint():
    # некоторые синхронные операции
    return {'status': 'completed'}

Проблема: вызов time.sleep(10) внутри синхронного обработчика блокирует весь поток, снижая пропускную способность. Решение: использовать asyncio.sleep() или перенести тяжелые операции в фоновые задачи.

Какие способы интеграции с базами данных поддерживает FastAPI?

FastAPI не ограничивает выбор ORM. Наиболее популярными являются:

  • Async SQLAlchemy 2.0 - для реляционных баз (PostgreSQL, MySQL).
  • Tortoise-ORM - асинхронный ORM с синтаксисом, напоминающим Django ORM.
  • Beanie - ODM для MongoDB на основе Pydantic.

Пример настройки асинхронной сессии SQLAlchemy:

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

DATABASE_URL = 'postgresql+asyncpg://user:pass@localhost/db'
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db():
    async with async_session_maker() as session:
        yield session

Использование в маршруте:

from fastapi import Depends
from sqlalchemy import select

@app.get('/items')
async def get_items(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Item))
    return result.scalars().all()

Частая ошибка: Session is not bound - сессия создается без контекста. Решение: использовать sessionmaker с привязкой к engine.

Как выполнять валидацию запросов без Pydantic?

Хотя Pydantic является неотъемлемой частью FastAPI, теоретически можно использовать стандартные dataclasses с методами валидации. Однако такой подход лишает автоматической документации и строгой типизации. Пример:

from dataclasses import dataclass

@dataclass
class Item:
    name: str
    price: float

@app.post('/items')
async def create_item(item: Item):
    return item

FastAPI преобразует JSON в датакласс, но не предоставляет детальной валидации. Рекомендуется использовать Pydantic для продакшена.

Как реализовать аутентификацию с помощью JWT токенов?

Стандартный подход использует библиотеки python-jose, passlib и OAuth2PasswordBearer. Пример:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext

SECRET_KEY = 'your-secret'
ALGORITHM = 'HS256'
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='token')

pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')

def create_access_token(data: dict):
    to_encode = data.copy()
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(status_code=401, detail='Invalid token')

@app.get('/protected')
async def protected_route(current_user = Depends(verify_token)):
    return {'user': current_user}

Ошибка: Invalid token при корректном токене - возможно, неверно указан алгоритм или секретный ключ.

Какие варианты развертывания FastAPI-приложений существуют?

Приложение можно запускать с помощью uvicorn, gunicorn (с uvicorn workers), в Docker контейнере или использовать платформы наподобие Heroku, Railway, AWS Lambda (через mangum). Каждый вариант имеет свои преимущества и особенности настройки.

Дополнительные практические примеры

Пример 1: WebSocket соединение

FastAPI поддерживает WebSocket для двусторонней связи. Пример простого эхо-сервера:

Пример
from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f'Вы отправили: {data}')

Запуск сервера и подключение через JavaScript или wscat.

В консоли: wscat -c ws://localhost:8000/ws
> Привет
< Вы отправили: Привет

Пример 2: Фоновая задача с использованием BackgroundTasks

Иногда нужно выполнить действие после отправки ответа клиенту (например, отправка письма). FastAPI предоставляет встроенную возможность через BackgroundTasks.

Пример
from fastapi import BackgroundTasks

def write_log(message: str):
    with open('log.txt', 'a') as f:
        f.write(f'{message}\n')

@app.post('/send-notification')
async def send_notification(background_tasks: BackgroundTasks):
    background_tasks.add_task(write_log, 'Уведомление отправлено')
    return {'status': 'ok'}

Фоновая задача запускается после возврата ответа, не задерживая его.

Пример 3: Пагинация с использованием Limit/Offset

Для списков используется query-параметры limit и offset. Расширенный пример с инъекцией зависимостей:

Пример
from fastapi import Query, Depends
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

async def paginate(db: AsyncSession, model, skip: int = 0, limit: int = 10):
    query = select(model).offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()

@app.get('/items')
async def list_items(skip: int = Query(0, ge=0), limit: int = Query(10, le=100), db = Depends(get_db)):
    items = await paginate(db, Item, skip, limit)
    return items

Пример 4: Загрузка файлов с валидацией

FastAPI использует UploadFile для работы с загружаемыми файлами.

Пример
from fastapi import File, UploadFile

@app.post('/upload/')
async def upload_file(file: UploadFile = File(...)):
    contents = await file.read()
    # обработка содержимого
    return {'filename': file.filename, 'size': len(contents)}

Можно добавить проверку типа файла через file.content_type.

Пример 5: Настройка CORS для доступа с другого домена

Использование CORSMiddleware из fastapi.middleware.cors.

Пример
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=['https://frontend.example.com'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)

Пример 6: Middleware для логирования времени ответа

Собственный middleware позволяет перехватывать запросы и ответы.

Пример
from fastapi import Request
import time

@app.middleware('http')
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers['X-Process-Time'] = str(process_time)
    return response

Пример 7: Тестирование с pytest и httpx

Для тестирования используется TestClient из Starlette (встроен в FastAPI).

Пример
from fastapi.testclient import TestClient

client = TestClient(app)

def test_read_main():
    response = client.get('/')
    assert response.status_code == 200
    assert response.json() == {'message': 'Hello World'}

Для асинхронных тестов нужно использовать pytest-asyncio.

Пример 8: Docker-образ для FastAPI приложения

Пример Dockerfile:

Пример
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Сборка и запуск: docker build -t myapi . && docker run -p 8000:8000 myapi.

Разработка бэкенда на FastAPI - comments

En
Fastapi python backend (python)