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

Создание собственного API на Python

Разработчику Архитектору

Разработка своего API

Выбор фреймворка для API на Python

Flask

Flask — это легковесный микрофреймворк, идеально подходящий для небольших проектов, прототипирования и обучения. Он предоставляет минимум встроенных функций, но благодаря своей гибкости позволяет подключать только те компоненты, которые действительно нужны. Для создания API на Flask достаточно определить маршруты с помощью декораторов и вернуть JSON-ответ с помощью функции jsonify.

Преимущества Flask:

  • Простота освоения.
  • Минимальный объем зависимостей.
  • Полный контроль над архитектурой.
  • Богатая экосистема расширений (Flask-RESTful, Flask-JWT, Flask-SQLAlchemy).

Недостатки:

  • Требуется больше ручной работы для реализации типичных функций (валидация, сериализация, пагинация).
  • Отсутствие встроенной поддержки автоматической документации.

FastAPI

Подробный раздел: FastAPI · Первая программа на FastAPI · FastAPI и база данных.

FastAPI — современный фреймворк, ориентированный на высокую производительность и удобство разработки. Он основан на стандартах OpenAPI и JSON Schema, что позволяет автоматически генерировать интерактивную документацию (через Swagger UI и ReDoc). FastAPI использует аннотации типов Python для валидации входных данных и сериализации выходных, что делает код самодокументируемым и менее подверженным ошибкам.

Преимущества FastAPI:

  • Автоматическая валидация и сериализация.
  • Встроенная поддержка асинхронного кода.
  • Генерация документации "из коробки".
  • Высокая скорость выполнения благодаря использованию Starlette и Pydantic.

Недостатки:

  • Требует понимания аннотаций типов и асинхронного программирования.
  • Меньше готовых решений для сложных сценариев по сравнению с Django.

Django REST Framework (DRF)

Django REST Framework — расширение для Django, превращающее его в мощный инструмент для построения API. DRF предоставляет сериализаторы, представления, классы разрешений, аутентификации, пагинации и фильтрации. Он отлично подходит для крупных проектов, где уже используется Django в качестве основного фреймворка.

Преимущества DRF:

  • Глубокая интеграция с Django ORM.
  • Поддержка сложных бизнес-логик и отношений между моделями.
  • Готовые решения для аутентификации (Token, Session, OAuth2).
  • Расширяемость и зрелая экосистема.

Недостатки:

  • Более высокий порог входа.
  • Избыточность для простых API.
  • Зависимость от архитектуры Django.

Архитектура типичного API на Python

Независимо от выбранного фреймворка, структура проекта обычно включает следующие компоненты:

Маршрутизация — определение URL-путей и сопоставление их с обработчиками (функциями или классами). Каждый маршрут соответствует определённому ресурсу или действию.

Обработчики запросов — функции, которые принимают входные данные, вызывают бизнес-логику и формируют ответ. Они отвечают за проверку прав доступа, обработку ошибок и преобразование данных.

Сериализация и десериализация — процесс преобразования объектов Python в формат, пригодный для передачи по сети (обычно JSON), и обратно. Сериализаторы также выполняют валидацию входных данных.

Бизнес-логика — ядро приложения, содержащее правила обработки данных, взаимодействия с базой данных, вызова внешних сервисов. Эта часть должна быть максимально независимой от деталей реализации API.

Слой доступа к данным — чаще всего это ORM (Object-Relational Mapper), такой как SQLAlchemy (для Flask и FastAPI) или Django ORM. ORM позволяет работать с базой данных через объекты Python, избегая прямого написания SQL-запросов.

Обработка ошибок — централизованный механизм перехвата исключений и возврата стандартизированных сообщений об ошибках. Хороший API никогда не возвращает "голые" трассировки стека.

Аутентификация и авторизация — механизмы проверки личности пользователя и его прав на выполнение операций. Часто используются токены (JWT, Bearer), сессии или API-ключи.

Логирование и мониторинг — запись событий, запросов и ошибок для последующего анализа. Это критически важно для диагностики проблем в продакшене.


Практические шаги — создание первого API

Начнём с минимального, но рабочего примера на FastAPI — фреймворке, который сочетает простоту и мощь. Установка требует только одного пакета:

pip install fastapi uvicorn

Разбор фрагмента:

  • fastapi устанавливает фреймворк для создания HTTP API.
  • uvicorn добавляет ASGI-сервер, который запускает приложение в разработке и проде.

Создадим файл main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"message": "Добро пожаловать в ваш первый API!"}

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

Разбор фрагмента:

  • app = FastAPI() создаёт экземпляр приложения.
  • @app.get("/") и @app.get("/items/{item_id}") регистрируют GET-эндпоинты.
  • item_id: int автоматически валидируется как число из пути.
  • Параметр q: str = None читается из query string и становится необязательным.
  • Возврат словаря преобразуется FastAPI в JSON-ответ.

Запуск сервера:

uvicorn main:app --reload

Разбор фрагмента:

  • main:app означает "модуль main, объект приложения app".
  • Флаг --reload перезапускает сервер после сохранения файлов и ускоряет разработку.

После запуска приложение будет доступно по адресу http://127.0.0.1:8000. Документация автоматически генерируется по путям /docs (Swagger UI) и /redoc (ReDoc). Это демонстрирует одно из ключевых преимуществ FastAPI: разработчик получает интерактивную документацию без дополнительных усилий.

Аналогичный пример на Flask выглядит так:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/")
def home():
return jsonify({"message": "Добро пожаловать в ваш первый API!"})

@app.route("/items/<int:item_id>")
def get_item(item_id):
return jsonify({"item_id": item_id})

Разбор фрагмента:

  • Flask-версия строит похожий API, но без автоматической схемы OpenAPI.
  • <int:item_id> задаёт типизированный параметр маршрута на стороне Flask.
  • jsonify(...) формирует JSON и выставляет корректный Content-Type.

Запуск:

FLASK_APP=main.py flask run

Разбор фрагмента:

  • Переменная FLASK_APP указывает, какой файл содержит объект приложения.
  • flask run поднимает dev-сервер Flask для локальной проверки маршрутов.

Flask не предоставляет встроенную документацию, но её можно добавить с помощью расширений вроде Flask-Swagger или Flasgger.

В Django REST Framework процесс немного дольше, поскольку требует настройки проекта, но он оправдан при работе с большими системами. После создания проекта и приложения, определяются модели, сериализаторы и представления. DRF позволяет использовать как функциональные, так и классовые представления, а также готовые generic-классы для стандартных операций (ListCreateAPIView, RetrieveUpdateDestroyAPIView и другие).


Работа с данными — модели, ORM и сериализация

Большинство API взаимодействуют с базой данных. В Python это чаще всего делается через ORM — объектно-реляционный маппер, который отображает строки таблиц на объекты Python.

В FastAPI популярным выбором является SQLAlchemy в связке с Pydantic. Pydantic отвечает за валидацию и сериализацию, а SQLAlchemy — за запросы к базе. Пример модели пользователя:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
engine = create_engine("sqlite:///./test.db")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)

Разбор фрагмента:

  • create_engine(...) настраивает соединение с базой данных.
  • declarative_base() создаёт базовый класс для ORM-моделей.
  • Класс User описывает таблицу users и её поля.
  • unique=True на email гарантирует уникальность адреса в БД.

Pydantic-схема для входных и выходных данных:

from pydantic import BaseModel

class UserCreate(BaseModel):
name: str
email: str

class UserResponse(BaseModel):
id: int
name: str
email: str

class Config:
from_attributes = True # заменяет orm_mode в новых версиях

Разбор фрагмента:

  • UserCreate описывает входные поля для создания пользователя.
  • UserResponse фиксирует структуру ответа API.
  • from_attributes = True разрешает собирать ответ из ORM-объекта, а не только из словаря.

Обработчик создания пользователя:

Код ITЗагрузка примера кода…

Разбор фрагмента:

  • Depends(get_db) внедряет сессию БД как зависимость на время запроса.
  • Проверка db.query(...).first() не допускает дублирования email.
  • db.add(...) и db.commit() сохраняют объект в таблицу.
  • db.refresh(new_user) подтягивает данные после коммита, включая id.

Этот код демонстрирует сквозной поток: клиент отправляет JSON → FastAPI валидирует его через Pydantic → бизнес-логика проверяет уникальность → данные сохраняются через SQLAlchemy → ответ сериализуется обратно в JSON.

В Django REST Framework этот процесс инкапсулирован ещё глубже. Сериализаторы наследуются от serializers.ModelSerializer, а представления — от generics.ListCreateAPIView и подобных. Это ускоряет разработку, но требует понимания внутренней архитектуры фреймворка.


Обработка ошибок и стандартизация ответов

Хороший API возвращает структурированные ошибки. Например, вместо простого текста "Invalid email" клиент получает:

{
"error": {
"code": "invalid_email",
"message": "Формат электронной почты некорректен",
"field": "email"
}
}

В FastAPI это достигается через пользовательские исключения и обработчики:

from fastapi import HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=400,
content={"error": {"code": "validation_failed", "message": str(exc)}}
)

Разбор фрагмента:

  • @app.exception_handler(...) регистрирует единый формат ошибок валидации.
  • JSONResponse(...) позволяет задать свой JSON-контракт и статус-код.
  • Такой обработчик стабилизирует интеграции с фронтендом и внешними клиентами.

В Flask используется декоратор @app.errorhandler, в DRF — настройка EXCEPTION_HANDLER в конфигурации.

Стандартизация формата ответов (включая успешные) повышает предсказуемость. Многие команды используют обёртку вида:

{
"data": { ... },
"meta": { "timestamp": "...", "version": "1.0" }
}

или

{
"success": true,
"result": { ... }
}

Такой подход особенно полезен при интеграции с фронтендом или мобильными приложениями.


Аутентификация и безопасность

API должен защищать данные от несанкционированного доступа. Наиболее распространённые методы:

  • API-ключи — простой способ идентификации клиента. Передаются в заголовке X-API-Key.
  • JWT (JSON Web Token) — токен, содержащий закодированную информацию о пользователе и сроках действия. Подписывается секретным ключом, что гарантирует подлинность.
  • OAuth2 — стандарт делегированного доступа, часто используемый для авторизации через сторонние сервисы (Google, GitHub).

FastAPI предоставляет встроенные зависимости для всех этих сценариев. Например, защита эндпоинта с помощью Bearer-токена:

from fastapi.security import HTTPBearer
from fastapi import Depends

security = HTTPBearer()

@app.get("/protected")
def protected_route(token: str = Depends(security)):
# Проверка токена
return {"message": "Доступ разрешён"}

Разбор фрагмента:

  • HTTPBearer() извлекает bearer-токен из заголовка Authorization.
  • Depends(security) делает проверку обязательной для маршрута.
  • Внутри обработчика токен дополнительно валидируют подписью и сроком действия.

Важно также применять общие меры безопасности:

  • Ограничение частоты запросов (rate limiting).
  • Валидация всех входных данных.
  • Использование HTTPS в продакшене.
  • Отключение детальных сообщений об ошибках в production-среде.

Тестирование API

Тестирование — неотъемлемая часть жизненного цикла API. Python предлагает несколько уровней:

  • Unit-тесты — проверяют отдельные функции и компоненты (например, сериализаторы).
  • Интеграционные тесты — проверяют взаимодействие между слоями (например, вызов эндпоинта с последующей проверкой состояния базы данных).
  • End-to-end тесты — имитируют реальные запросы от клиента.

FastAPI поддерживает тестирование через TestClient:

from fastapi.testclient import TestClient

client = TestClient(app)

def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Добро пожаловать в ваш первый API!"}

Flask использует test_client(), DRF — APIClient из rest_framework.test.

Автоматизированные тесты должны запускаться при каждом коммите, что обеспечивается CI/CD-системами (GitHub Actions, GitLab CI и другими).


Документирование API

Документация — это обязательная часть любого публичного или внутреннего API. Без неё интеграция становится медленной, ошибки — частыми, а поддержка — затратной. Современные фреймворки Python позволяют генерировать документацию автоматически, основываясь на аннотациях кода.

OpenAPI (ранее Swagger) — это стандарт описания RESTful API. Он определяет структуру запросов, параметров, тел, ответов, схем данных и возможных ошибок. FastAPI генерирует спецификацию OpenAPI в реальном времени и предоставляет два интерфейса для её просмотра:

  • Swagger UI (/docs) — интерактивный веб-интерфейс, где можно отправлять запросы прямо из браузера.
  • ReDoc (/redoc) — более читаемый, документоориентированный просмотрщик.

В Flask документацию можно добавить с помощью расширений:

  • Flasgger — генерирует OpenAPI-спецификацию на основе docstring’ов.
  • apispec — более гибкий инструмент, совместимый с Marshmallow.

В Django REST Framework используется модуль drf-yasg (Yet Another Swagger Generator) или встроенный SchemaGenerator, который работает с классами представлений и сериализаторами.

Хорошая документация содержит:

  • Описание каждого эндпоинта: назначение, примеры использования.
  • Спецификацию всех параметров — тип, обязательность, формат, допустимые значения.
  • Примеры успешных и ошибочных ответов.
  • Информацию об аутентификации.
  • Схемы данных в формате JSON Schema.

Автоматическая генерация гарантирует, что документация всегда соответствует текущему состоянию кода. Это особенно важно при частых изменениях.


Версионирование API

Со временем требования к API меняются — добавляются новые поля, удаляются старые, изменяется логика. Чтобы не нарушать работу существующих клиентов, применяется версионирование.

Наиболее распространённые подходы:

  1. Версия в URL:
    GET /v1/users
    GET /v2/users
    Этот метод прост, нагляден и легко кэшируется. Он рекомендован большинством крупных платформ (GitHub, Stripe).

  2. Версия в заголовке запроса:
    Accept: application/vnd.myapi.v2+json
    Этот способ сохраняет чистоту URL, но сложнее для отладки и требует явной настройки на стороне клиента.

  3. Версия в параметре строки запроса:
    GET /users?version=2
    Такой подход менее предпочтителен, так как нарушает принцип идемпотентности и усложняет кэширование.

При проектировании API следует заранее предусмотреть возможность версионирования. Например, в FastAPI можно создать отдельные подприложения для каждой версии:

from fastapi import FastAPI

app = FastAPI()
v1 = FastAPI()
v2 = FastAPI()

app.mount("/v1", v1)
app.mount("/v2", v2)

Разбор фрагмента:

  • Создаются отдельные подприложения для разных версий API.
  • app.mount("/v1", v1) и app.mount("/v2", v2) разводят маршруты по префиксам.
  • Такой подход позволяет развивать версии параллельно без поломки старых клиентов.

Каждая версия развивается независимо, но может использовать общие модели и бизнес-логику через импорты.


Развёртывание API

Локальный запуск — лишь первый шаг. Для продакшена требуется надёжное развёртывание.

WSGI и ASGI

  • Flask и Django работают по протоколу WSGI (Web Server Gateway Interface). Для них используются серверы: Gunicorn, uWSGI.
  • FastAPI и современные асинхронные приложения используют ASGI (Asynchronous Server Gateway Interface). Серверы — Uvicorn, Daphne, Hypercorn.

Типичный стек для FastAPI в production:

  • Uvicorn — ASGI-сервер.
  • Gunicorn — менеджер процессов, запускающий несколько воркеров Uvicorn.
  • Nginx — обратный прокси, обрабатывающий SSL, статические файлы, ограничение скорости.
  • Docker — контейнеризация для единообразного окружения.

Пример Dockerfile для FastAPI:

FROM python:3.11-slim

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

CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "main:app"]

Разбор фрагмента:

  • FROM python:3.11-slim задаёт компактный базовый образ Python.
  • COPY requirements.txt и pip install ... фиксируют зависимости контейнера.
  • CMD ... запускает Gunicorn с ASGI-воркерами Uvicorn для FastAPI-приложения.

Расширенный разбор с uvicorn, проверкой и job-контейнером — Dockerfile — 10 типовых образов.

Для Flask аналогичный файл использует gunicorn main:app.

Облачные платформы
API можно развернуть на:

  • Render, Fly.io, Railway — простые PaaS-решения с минимальной конфигурацией.
  • AWS Lambda + API Gateway — бессерверный подход (serverless), подходит для событийных или редко вызываемых API.
  • Google Cloud Run, Azure Container Instances — запуск контейнеров без управления инфраструктурой.

Выбор зависит от нагрузки, бюджета и требований к масштабируемости.


Мониторинг и логирование

В продакшене API должен быть наблюдаемым. Это достигается через:

  • Логирование — запись входящих запросов, исключений, времени выполнения. В Python используется модуль logging. Логи направляются в файлы, систему типа ELK (Elasticsearch, Logstash, Kibana) или облачные сервисы (CloudWatch, Datadog).
  • Метрики — количество запросов, время ответа, частота ошибок. Инструменты: Prometheus + Grafana.
  • Трассировка — отслеживание цепочки вызовов в распределённой системе. Используется OpenTelemetry.

FastAPI и Flask легко интегрируются с этими системами через middleware — промежуточные слои, которые перехватывают каждый запрос и ответ.

Пример middleware для логирования в FastAPI:


import time

from fastapi import Request

@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
print(f"{request.method} {request.url.path}{response.status_code}{process_time:.3f}s")
return response

Разбор фрагмента:

  • @app.middleware("http") подключает обработчик ко всем входящим запросам.
  • call_next(request) передаёт запрос дальше по цепочке и возвращает ответ.
  • Разница времени до и после вызова даёт latency конкретного запроса.
  • Лог с методом, путём и кодом ответа помогает быстро диагностировать деградации.

Такой подход позволяет собирать данные без изменения основной логики приложения.


Лучшие практики проектирования API

  1. Используйте существительные, а не глаголы в путях: /users, а не /getUsers.
  2. Соблюдайте иерархию ресурсов: /users/123/orders/456.
  3. Возвращайте полезные HTTP-статусы: не используйте 200 OK для ошибок.
  4. Ограничьте размер ответа: применяйте пагинацию (limit, offset или курсорную).
  5. Поддерживайте частичное обновление: PATCH вместо полной замены через PUT.
  6. Избегайте вложенных JSON-структур глубже двух уровней — это усложняет парсинг.
  7. Предоставляйте ссылки на связанные ресурсы: &#123; "user": "/users/123" &#125;.
  8. Не возвращайте чувствительные данные (пароли, токены) в ответах.
  9. Поддерживайте CORS, если API вызывается из браузера.
  10. Пишите тесты до или одновременно с реализацией — это гарантирует стабильность контракта.

Как выбрать фреймворк под задачу

Практичное правило для старта:

  • Flask — когда нужен быстрый прототип, прозрачный код и контроль каждого слоя;
  • FastAPI — когда API-контракт, валидация и автодокументация являются центральной частью продукта;
  • DRF — когда проект уже в экосистеме Django и важны встроенные механизмы ORM, админки и зрелых политик доступа.

Решение лучше принимать по критериям команды — скорость онбординга, существующая инфраструктура, требования к масштабированию и безопасности.


Антипаттерны при разработке API

  • смешивать бизнес-логику и доступ к БД прямо в маршрутах;
  • отдавать разные форматы ошибок в разных эндпоинтах;
  • использовать HTTP 200 для ошибок валидации;
  • пропускать versioning до появления первых интеграций;
  • откладывать тесты до этапа "после релиза".

Избежать этих проблем помогает ранняя стандартизация контрактов, автотесты и единый стиль ответов.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.