Создание веб-сервисов с FastAPI: от основ до сложных решений
FastAPI представляет собой современный веб-фреймворк для языка Python, ориентированный на создание высокопроизводительных API. Он сочетает в себе простоту разработки, асинхронную обработку запросов и автоматическую генерацию документации OpenAPI. В данной статье рассматриваются ключевые аспекты разработки бэкенда с использованием FastAPI: установка, маршрутизация, валидация данных, работа с базами данных, аутентификация, тестирование и развертывание.
Основные возможности FastAPI
Как создать минимальное рабочее приложение на FastAPI?
Стандартный подход предполагает использование асинхронных функций (async def) для обработчиков маршрутов, что обеспечивает максимальную производительность при большом количестве одновременных подключений. Установка выполняется через pip:
pip install fastapi uvicornFastapi 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.