Разработка современных веб-API на FastAPI

Практический курс по созданию высокопроизводительных асинхронных веб-сервисов на Python. Вы изучите основы фреймворка, работу с базами данных, валидацию и деплой приложений.

1. Основы FastAPI: установка, структура проекта и создание первого Hello World приложения

Основы FastAPI: установка, структура проекта и создание первого Hello World приложения

Добро пожаловать в курс «Разработка современных веб-API на FastAPI». Это первая статья, с которой начнется ваше погружение в мир высокопроизводительной веб-разработки на Python. Сегодня мы разберем фундамент: что такое FastAPI, как подготовить рабочее окружение и как написать ваше первое работающее приложение.

Что такое FastAPI и почему он так популярен?

FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API на Python 3.8+, основанный на стандартных подсказках типов Python (type hints).

Ключевые особенности, которые сделали его стандартом индустрии:

* Скорость: Он стоит в одном ряду с NodeJS и Go благодаря использованию Starlette (для веб-части) и Pydantic (для валидации данных). * Простота разработки: Уменьшает количество ошибок примерно на 40% и сокращает время написания кода. * Автоматическая документация: Одна из самых любимых функций разработчиков. Как только вы пишете код, FastAPI автоматически генерирует интерактивную документацию (Swagger UI и ReDoc).

!Архитектура работы FastAPI: от запроса клиента через сервер Uvicorn к самому приложению

Подготовка рабочего окружения

Перед тем как писать код, нам нужно подготовить «почву». Мы будем использовать командную строку (терминал) и редактор кода (рекомендуем VS Code или PyCharm).

1. Проверка версии Python

FastAPI требует Python версии 3.8 или выше. Проверьте вашу версию командой:

Если версия ниже 3.8, вам необходимо обновить Python.

2. Создание виртуального окружения

В Python принято изолировать зависимости каждого проекта. Это позволяет избежать конфликтов библиотек между разными проектами. Создадим папку для нашего проекта и виртуальное окружение внутри неё.

Для Windows:

Для macOS и Linux:

После активации вы увидите (venv) в начале строки терминала. Это означает, что теперь все устанавливаемые библиотеки будут храниться локально в этой папке.

Установка FastAPI и Uvicorn

Теперь, когда окружение готово, установим необходимые библиотеки. Нам понадобятся два ключевых компонента:

  • fastapi: Сам фреймворк, который мы будем использовать для создания API.
  • uvicorn: Это ASGI-сервер, который необходим для запуска нашего приложения. FastAPI сам по себе не умеет «слушать» сеть, ему нужен сервер, который будет принимать HTTP-запросы и передавать их в приложение.
  • Выполните команду:

    > ASGI (Asynchronous Server Gateway Interface) — это стандартный интерфейс между веб-серверами, фреймворками и приложениями на Python, поддерживающий асинхронность.

    Структура простейшего проекта

    Для начала нам не нужна сложная структура папок. Достаточно одного файла. Создайте в папке fastapi_course файл с именем main.py.

    Ваша директория должна выглядеть так:

    Пишем первое приложение Hello World

    Откройте файл main.py в вашем редакторе кода и напишите следующий код:

    Давайте разберем каждую строчку, чтобы понимать, что происходит «под капотом».

    Импорт и создание приложения

    Здесь мы импортируем класс FastAPI и создаем его экземпляр в переменной app. Эта переменная app будет главной точкой входа для всего нашего приложения. Именно к ней мы будем обращаться, чтобы настроить маршруты и конфигурацию.

    Декоратор операции пути

    Эта конструкция называется декоратором. Она говорит FastAPI, что функция, написанная ниже, должна обрабатывать запросы, которые приходят по определенному адресу.

    * @app — обращение к нашему приложению. * .get — HTTP-метод. Это означает, что функция сработает только тогда, когда придет GET-запрос (обычный запрос, когда вы открываете страницу в браузере). * ("/") — это путь (path). Символ / означает корневой адрес сайта (например, http://mysite.com/).

    Функция обработки пути

    Это обычная Python-функция. Ключевое слово async делает её асинхронной. FastAPI прекрасно работает и с обычными функциями (def), но использование async позволяет добиться максимальной производительности при работе с базами данных или внешними API.

    Функция возвращает словарь (dict). FastAPI автоматически преобразует этот словарь в формат JSON, который является стандартом для веб-API.

    Запуск приложения

    Мы написали код, но как его запустить? Здесь в игру вступает Uvicorn.

    Вернитесь в терминал (убедитесь, что вы находитесь в папке с файлом main.py и виртуальное окружение активно) и выполните команду:

    Разберем эту команду:

    * uvicorn: имя запускаемого сервера. * main: имя файла Python (модуля) без расширения .py. * app: имя переменной, которую мы создали внутри файла (app = FastAPI()). * --reload: флаг, который говорит серверу автоматически перезагружаться при любых изменениях в коде. Это очень удобно при разработке, но никогда не используйте этот флаг на реальном сервере (в продакшене).

    После запуска вы увидите примерно такой вывод:

    Теперь откройте браузер и перейдите по адресу: http://127.0.0.1:8000.

    Вы увидите JSON-ответ:

    Поздравляю! Вы только что создали и запустили свой первый API.

    Интерактивная документация

    Теперь перейдем к магии FastAPI. Не останавливая сервер, перейдите в браузере по адресу:

    http://127.0.0.1:8000/docs

    Вы увидите автоматическую интерактивную документацию, созданную с помощью Swagger UI.

    !Интерфейс автоматической документации Swagger UI, доступный по адресу /docs

    Здесь вы можете:

  • Видеть все доступные пути вашего API.
  • Нажать кнопку Try it out, затем Execute, и отправить реальный запрос к вашему API прямо из браузера.
  • Увидеть ответ сервера, код статуса (например, 200) и заголовки.
  • Также доступна альтернативная документация по адресу http://127.0.0.1:8000/redoc.

    Расширение примера: Приветствие по имени

    Давайте немного усложним задачу. Добавим возможность приветствовать конкретного пользователя. Измените ваш main.py, добавив новый маршрут:

    Обратите внимание на конструкцию /hello/{name}. Фигурные скобки означают, что name — это параметр пути. Мы также добавили аргумент name: str в функцию. Благодаря типизации (: str), FastAPI знает, что этот параметр должен быть строкой.

    Сохраните файл. Сервер перезагрузится автоматически (благодаря --reload).

    Теперь перейдите по адресу http://127.0.0.1:8000/hello/FastAPI.

    Вы получите:

    Резюме

    В этой статье мы:

  • Узнали, что FastAPI — это быстрый и современный фреймворк.
  • Настроили окружение и установили fastapi и uvicorn.
  • Написали простейшее приложение и запустили его.
  • Познакомились с автоматической документацией /docs.
  • В следующей статье мы углубимся в параметры запросов и валидацию данных, чтобы научиться передавать и обрабатывать более сложную информацию.

    2. Валидация данных и Pydantic: работа с параметрами запроса и моделями данных

    Валидация данных и Pydantic: работа с параметрами запроса и моделями данных

    Добро пожаловать во вторую часть нашего курса «Разработка современных веб-API на FastAPI». В предыдущей статье мы научились запускать сервер и создавать простейшие маршруты. Однако реальные приложения редко ограничиваются простым «Hello World». Они принимают данные от пользователей, обрабатывают их и сохраняют в базы данных.

    Главная проблема при работе с входящими данными — это доверие. Никогда нельзя доверять данным, пришедшим от клиента. Пользователь может отправить строку вместо числа, отрицательную цену или вообще пустой JSON. В старых фреймворках разработчикам приходилось писать десятки строк кода с if-else, чтобы проверить каждый параметр. FastAPI решает эту проблему элегантно и эффективно, используя библиотеку Pydantic.

    Сегодня мы разберем, как принимать параметры запроса, как описывать сложные структуры данных и как заставить FastAPI автоматически проверять (валидировать) всё, что прилетает на ваш сервер.

    Параметры запроса (Query Parameters)

    Вы наверняка видели URL-адреса, которые выглядят так: https://example.com/search?q=fastapi&page=2. Всё, что идет после вопросительного знака ?, называется параметрами запроса (query parameters). Они представляют собой пары «ключ-значение», разделенные амперсандом &.

    В FastAPI параметры запроса объявляются очень просто: если вы добавляете аргументы в функцию обработки пути, и эти аргументы не являются частью пути (не указаны в фигурных скобках {}), FastAPI автоматически воспринимает их как query-параметры.

    Рассмотрим пример пагинации (постраничного вывода):

    Разберем, что здесь происходит:

  • Мы объявили аргументы skip и limit.
  • Мы указали их типы: int. Это критически важно.
  • Мы задали значения по умолчанию: 0 и 10.
  • Теперь, если клиент сделает запрос GET /items/, FastAPI использует значения по умолчанию (skip=0, limit=10). Если клиент запросит GET /items/?skip=20&limit=5, переменные в функции примут соответствующие значения.

    Магия автоматического преобразования типов

    Самое интересное начинается, когда мы смотрим на типы данных. URL — это всегда строка. Но мы указали int. FastAPI (с помощью Pydantic) автоматически попытается преобразовать строку в число.

    Если пользователь отправит GET /items/?skip=foo, FastAPI вернет ошибку:

    Это избавляет вас от необходимости вручную проверять типы данных. Вы сразу работаете с числами.

    Логические параметры (Boolean)

    Часто в API нужны флаги (да/нет). FastAPI умеет умно обрабатывать bool типы.

    Если вы объявите параметр как bool, клиент может передать: * 1, true, True, on, yes — всё это превратится в Python True. * 0, false, False, off, no — превратится в False.

    Введение в Pydantic

    Параметры запроса хороши для фильтрации или сортировки, но что делать, если нам нужно отправить на сервер сложный объект? Например, данные для регистрации пользователя или создания товара. Здесь на сцену выходит тело запроса (Request Body) и модели Pydantic.

    Pydantic — это библиотека для парсинга и валидации данных. Она использует стандартные подсказки типов Python для определения схемы данных.

    !Pydantic выступает в роли строгого контролера, который превращает сырые данные в надежные объекты Python.

    Чтобы отправить данные (обычно в формате JSON) на сервер, мы используем метод POST (или PUT, PATCH).

    Создание модели данных

    Для начала нужно импортировать BaseModel из модуля pydantic и создать класс, наследуемый от него.

    Давайте проанализируем модель Item: * name: строка, обязательное поле (нет значения по умолчанию). * description: строка или None, необязательное поле (по умолчанию None). * price: дробное число, обязательное поле. * tax: дробное число или None, необязательное поле.

    Когда вы указываете item: Item в аргументах функции, FastAPI делает следующее:

  • Читает тело запроса как JSON.
  • Преобразует типы (если пришло "price": "10.5", оно станет float 10.5).
  • Валидирует данные (проверяет обязательные поля).
  • Передает вам готовый объект item.
  • Теперь вы можете обращаться к полям через точку, как к атрибутам класса: item.name, item.price. Ваш редактор кода (IDE) будет подсказывать названия полей, потому что теперь он знает структуру данных.

    Использование данных модели

    Давайте напишем небольшую бизнес-логику. Представим, что нам нужно рассчитать итоговую стоимость товара с учетом налога.

    Формула расчета будет выглядеть так:

    где — итоговая цена, — базовая цена товара, а — ставка налога (например, 0.2 для 20%).

    Реализуем это в коде:

    Здесь мы использовали метод .dict(), который есть у всех моделей Pydantic, чтобы превратить объект обратно в словарь.

    Валидация полей с помощью Field

    Иногда простого указания типа (int или str) недостаточно. Например, цена не может быть отрицательной, а название товара не должно превышать 50 символов.

    Для таких уточнений Pydantic предоставляет функцию Field.

    Обратите внимание на параметры внутри Field: * gt=0: (greater than) значение должно быть строго больше 0. Также существуют ge (больше или равно), lt (меньше), le (меньше или равно). * max_length=300: ограничивает длину строки. * description: добавляет описание поля, которое попадет в автоматическую документацию Swagger UI.

    Если клиент попытается отправить товар с ценой -5, FastAPI вернет ошибку 422 Unprocessable Entity с понятным описанием, что поле price должно быть больше 0.

    Совместное использование параметров пути, запроса и тела

    FastAPI позволяет смешивать все типы параметров в одной функции. Фреймворк достаточно умен, чтобы различить их:

  • Если параметр объявлен в пути{}), это Path Parameter.
  • Если параметр типа Pydantic Model, это Request Body.
  • Если параметр простого типа (int, str) и не в пути, это Query Parameter.
  • Пример:

    В этом примере: * item_id: берется из URL (например, /items/5). * item: берется из тела JSON-запроса. * q: берется из параметров запроса (например, ?q=search).

    Почему это меняет правила игры?

    Использование Pydantic вместе с FastAPI дает несколько колоссальных преимуществ:

  • Безопасность: Вы отсекаете некорректные данные на входе. Ваша бизнес-логика никогда не упадет из-за того, что вместо списка пришла строка.
  • Чистота кода: Вам не нужно писать валидаторы вручную. Всё декларативно.
  • IDE Support: Автодополнение работает везде.
  • Документация: Все ограничения (например, max_length или gt=0) автоматически отображаются в /docs.
  • Заключение

    Сегодня мы сделали огромный шаг вперед. Мы перешли от простых GET-запросов к полноценному обмену данными. Мы научились: * Принимать и фильтровать параметры запроса. * Описывать структуры данных с помощью классов Pydantic. * Настраивать тонкую валидацию с помощью Field.

    В следующей статье мы разберем, как правильно структурировать большие проекты, чтобы не хранить весь код в одном файле main.py, и познакомимся с APIRouter.

    3. Взаимодействие с базами данных и система внедрения зависимостей Dependency Injection

    Взаимодействие с базами данных и система внедрения зависимостей Dependency Injection

    Добро пожаловать в третью часть курса «Разработка современных веб-API на FastAPI». В предыдущих статьях мы научились создавать базовое приложение и валидировать входящие данные с помощью Pydantic. Однако, до сих пор наши данные жили лишь мгновение — пока обрабатывался запрос. Как только сервер отправлял ответ, данные исчезали.

    Чтобы создать полноценное приложение, нам нужно научиться сохранять информацию надолго. Для этого мы подключим базу данных. Но прежде чем мы начнем писать SQL-запросы, нам нужно разобраться с одной из самых мощных и важных концепций FastAPI — Внедрением зависимостей (Dependency Injection).

    Что такое Dependency Injection (DI)?

    В программировании термин «внедрение зависимостей» часто звучит пугающе сложно, но на практике это очень простая и элегантная идея.

    Представьте, что вы пришли в ресторан. Чтобы поесть, вам нужны столовые приборы. Есть два способа их получить:

  • Без DI: Вы достаете из кармана свою собственную вилку и ложку, которые принесли из дома. Это неудобно, вам нужно носить их с собой, мыть и следить за ними.
  • С DI: Вы садитесь за стол, и официант приносит вам чистые приборы. Вам не нужно знать, где они хранились и кто их мыл. Вы просто заявляете, что они вам нужны, и вам их предоставляют.
  • В коде это работает так же. Вместо того чтобы создавать подключение к базе данных или искать текущего пользователя внутри каждой функции, вы просто указываете в аргументах функции: «Мне нужна база данных» или «Мне нужен текущий пользователь». FastAPI сам позаботится о том, чтобы создать эти объекты и передать их вам.

    Зачем это нужно?

    * Чистота кода: Ваша бизнес-логика не захламлена кодом настройки соединений. * Тестируемость: При написании тестов вы легко можете подменить реальную базу данных на тестовую, просто передав другую зависимость. * Переиспользование: Вы пишете логику получения пользователя один раз и используете её во всем приложении.

    Использование Depends

    В FastAPI за внедрение зависимостей отвечает специальный класс Depends. Давайте посмотрим на простой пример.

    Допустим, у нас есть параметры пагинации (страница и количество элементов), которые повторяются во многих маршрутах.

    Что здесь происходит:

  • Мы создали функцию common_parameters, которая принимает аргументы запроса.
  • В функциях read_items и read_users мы не прописываем аргументы skip и limit заново.
  • Мы используем Depends(common_parameters). FastAPI видит это, вызывает функцию common_parameters, забирает её результат и передает его в переменную commons.
  • Это простейший пример, но именно этот механизм позволит нам эффективно работать с базой данных.

    Подключение базы данных с SQLAlchemy

    FastAPI не привязан к конкретной базе данных, но стандартом де-факто в мире Python является библиотека SQLAlchemy. Это мощная ORM (Object-Relational Mapping), которая позволяет работать с таблицами базы данных как с обычными объектами Python.

    !SQLAlchemy выступает мостом между кодом на Python и базой данных.

    Для начала установим библиотеку:

    Настройка соединения

    Создадим файл database.py. Здесь мы настроим подключение. Для простоты будем использовать SQLite — это база данных, которая хранится в одном файле и не требует установки отдельного сервера.

    Создание моделей базы данных

    Теперь нам нужно описать, как будут выглядеть наши таблицы. Создадим файл models.py.

    Важно: Не путайте Pydantic-модели (которые мы использовали для валидации в прошлой статье) и SQLAlchemy-модели. Pydantic — это схема данных (как они выглядят в JSON), а SQLAlchemy — это структура таблицы в базе данных.

    Здесь мы создали класс Item, который наследуется от Base. Мы указали имя таблицы items и перечислили колонки.

    Создание Pydantic схем

    Чтобы принимать данные от пользователя и отдавать их обратно, нам все еще нужны Pydantic модели. Создадим файл schemas.py.

    Обратите внимание на from_attributes = True (в старых версиях Pydantic это называлось orm_mode). Это критически важно, так как SQLAlchemy возвращает объекты, а не словари.

    Главный секрет: Dependency для получения сессии БД

    Теперь самое интересное. Нам нужно, чтобы при каждом запросе открывалась сессия с базой данных, выполнялась работа, а затем сессия обязательно закрывалась, даже если произошла ошибка. Если не закрывать сессии, база данных быстро «захлебнется» от количества открытых соединений.

    Мы реализуем это в файле main.py с помощью генератора и ключевого слова yield.

    Разберем функцию get_db:

  • db = SessionLocal(): Мы создаем новую сессию.
  • yield db: Мы «отдаем» эту сессию функции, которая обрабатывает запрос. На этом выполнение get_db приостанавливается.
  • Функция обработки пути (path operation) делает свои дела.
  • finally: db.close(): Как только запрос завершен (успешно или с ошибкой), выполнение возвращается в get_db после yield, и срабатывает блок finally, закрывающий соединение.
  • Реализация CRUD операций

    Теперь у нас есть всё необходимое, чтобы написать реальный код сохранения и получения данных.

    Создание записи (Create)

    В аргументах функции мы видим db: Session = Depends(get_db). Теперь внутри функции переменная db — это готовая к работе сессия базы данных.

    Чтение записей (Read)

    Здесь мы используем методы SQLAlchemy: .query(), .filter(), .offset(), .limit() и .all() или .first() для построения SQL-запроса.

    Резюме

    В этой статье мы разобрали фундаментальные концепции построения бэкенда:

  • Dependency Injection: Мы научились использовать Depends для внедрения логики и объектов в наши функции.
  • SQLAlchemy: Мы настроили подключение к базе данных SQLite и создали модели.
  • Сессии: Мы создали безопасный механизм получения и закрытия сессий базы данных с помощью yield.
  • CRUD: Мы реализовали создание и чтение данных, которые теперь сохраняются навсегда.
  • В следующей статье мы поговорим о структурировании проекта, так как хранить весь код в одном файле main.py становится неудобно, и научимся использовать APIRouter для разделения логики.

    4. Безопасность приложения: реализация аутентификации и авторизации с использованием OAuth2 и JWT

    Безопасность приложения: реализация аутентификации и авторизации с использованием OAuth2 и JWT

    Добро пожаловать в четвертую часть курса «Разработка современных веб-API на FastAPI». В прошлых статьях мы построили фундамент: научились создавать маршруты, валидировать данные с Pydantic и сохранять их в базу данных с помощью SQLAlchemy. Теперь перед нами стоит критически важная задача — защита нашего приложения.

    Представьте, что вы построили дом (ваше API). У него есть двери (эндпоинты) и ценные вещи внутри (данные). Сейчас двери открыты для всех. Любой прохожий может зайти, взять что угодно или даже сломать мебель. Чтобы этого не произошло, нам нужен замок и список жильцов, которым разрешен вход.

    Сегодня мы разберем две ключевые концепции безопасности: аутентификацию и авторизацию, и реализуем их, используя современные стандарты OAuth2 и JWT.

    Аутентификация vs Авторизация

    Эти термины часто путают, но разница между ними принципиальна.

  • Аутентификация (Authentication) — это ответ на вопрос: «Кто ты?». Это процесс проверки личности пользователя. В реальной жизни это предъявление паспорта на границе.
  • Авторизация (Authorization) — это ответ на вопрос: «Что тебе можно делать?». Это проверка прав доступа. В реальной жизни это виза в паспорте, которая разрешает въезд в конкретную страну, но не дает права работать там президентом.
  • !Визуальное различие между проверкой личности и проверкой прав доступа.

    Что такое JWT (JSON Web Token)?

    Для передачи информации о пользователе между клиентом и сервером мы будем использовать JWT. Это открытый стандарт (RFC 7519), который определяет компактный и самодостаточный способ безопасной передачи информации.

    Представьте JWT как прозрачный чемоданчик с цифровой печатью. Любой может посмотреть, что внутри, но если кто-то попытается изменить содержимое, печать сломается, и сервер поймет, что данные подделаны.

    Токен состоит из трех частей, разделенных точками:

    Header.Payload.Signature

  • Header (Заголовок): Тип токена и алгоритм шифрования.
  • Payload (Полезная нагрузка): Данные (например, ID пользователя, его роль, время истечения токена).
  • Signature (Подпись): Гарантия того, что токен не был изменен.
  • Математически создание подписи можно выразить так:

    Где — итоговая подпись, — заголовок, — полезная нагрузка, а — секретный ключ, который знает только сервер. Функция смешивает данные и ключ, создавая уникальную строку.

    Подготовка окружения

    Нам понадобятся библиотеки для работы с криптографией и токенами. Установите их:

    * passlib[bcrypt]: Для хеширования паролей. * python-jose: Для генерации и проверки JWT токенов. * python-multipart: Для обработки данных формы входа (OAuth2 spec).

    Хеширование паролей

    Золотое правило безопасности: Никогда не храните пароли в открытом виде. Если злоумышленник украдет базу данных, он получит доступ ко всем аккаунтам.

    Вместо пароля мы храним его хеш. Хеш — это результат одностороннего преобразования строки.

    Создадим файл security.py для работы с паролями:

    Теперь, когда пользователь регистрируется, мы сохраняем результат get_password_hash(password). Когда он входит, мы сравниваем введенный пароль с сохраненным хешем через verify_password.

    Генерация JWT токена

    В том же файле security.py добавим логику создания токенов.

    Настройка OAuth2 в FastAPI

    FastAPI предоставляет встроенные инструменты для работы с OAuth2. Вернемся в main.py (или создадим отдельный файл auth.py, если вы разделяете логику).

    OAuth2PasswordBearer — это класс, который говорит FastAPI: «Смотри, мы используем схему безопасности, где клиент отправляет токен в заголовке Authorization: Bearer <token>. Если токена нет, верни 401 ошибку».

    Создание эндпоинта для входа (Login)

    Теперь создадим маршрут, который принимает логин/пароль и выдает токен.

    Обратите внимание на OAuth2PasswordRequestForm. Это специальная зависимость FastAPI, которая ожидает данные не в JSON, а в полях формы (username и password), как того требует стандарт OAuth2.

    Защита маршрутов: получение текущего пользователя

    Самая важная часть. Нам нужна функция-зависимость, которая:

  • Возьмет токен из запроса.
  • Расшифрует его и проверит подпись.
  • Достанет из токена email пользователя.
  • Найден пользователя в БД.
  • Использование защиты в эндпоинтах

    Теперь защитить любой маршрут проще простого. Просто добавьте зависимость get_current_user.

    Когда вы попытаетесь вызвать этот метод через Swagger UI (/docs), FastAPI автоматически покажет кнопку Authorize. Вы сможете ввести логин и пароль, получить токен, и браузер будет автоматически прикреплять его ко всем запросам.

    !Пошаговый процесс аутентификации и последующего авторизованного запроса.

    Резюме

    В этой статье мы реализовали полноценную систему безопасности:

  • Разобрали разницу между аутентификацией и авторизацией.
  • Узнали, как устроен JWT и почему он популярен.
  • Научились хешировать пароли с помощью bcrypt, чтобы защитить пользователей.
  • Настроили выдачу токенов через OAuth2 flow.
  • Создали зависимость get_current_user, которая защищает наши маршруты от неавторизованного доступа.
  • Теперь ваше API не просто функционально, но и безопасно. В следующей статье мы рассмотрим, как правильно структурировать проект, когда он разрастается до десятков файлов, используя APIRouter.

    5. Тестирование, асинхронность и развертывание приложения в Docker контейнере

    Тестирование, асинхронность и развертывание приложения в Docker контейнере

    Добро пожаловать в заключительную часть курса «Разработка современных веб-API на FastAPI». Мы прошли долгий путь: от создания первого «Hello World» до реализации сложной системы аутентификации с JWT и базой данных. Ваше приложение уже умеет многое, но готово ли оно к реальному миру?

    В профессиональной разработке написание кода — это только половина дела. Чтобы приложение жило долго и счастливо, оно должно быть:

  • Надежным: Мы должны быть уверены, что новые изменения не сломали старый функционал. Для этого нужны тесты.
  • Производительным: Оно должно эффективно обрабатывать множество одновременных запросов. Здесь нам поможет асинхронность.
  • Переносимым: Оно должно одинаково работать на вашем ноутбуке, на сервере коллеги и в облаке. Эту задачу решает Docker.
  • Сегодня мы превратим наш проект из «домашней поделки» в профессиональный продукт.

    Асинхронность: как обслуживать тысячи клиентов

    FastAPI называется «Fast» не просто так. Одной из его ключевых особенностей является нативная поддержка асинхронного программирования. Но что это такое?

    Аналогия с рестораном

    Представьте ресторан с одним официантом.

    Синхронный подход: Официант подходит к столику, принимает заказ, идет на кухню и ждет*, пока повар приготовит еду. Только когда блюдо готово, он относит его клиенту и идет к следующему столику. Если готовка занимает 20 минут, все остальные клиенты ждут. Асинхронный подход: Официант принимает заказ, передает его на кухню и, не дожидаясь* готовности, идет обслуживать следующий столик. Когда еда готова, кухня подает сигнал, и официант относит заказ.

    В веб-разработке «кухня» — это операции ввода-вывода (I/O): запросы к базе данных, чтение файлов, обращение к сторонним API. Именно эти операции занимают больше всего времени.

    !Схематичное изображение эффективности асинхронного подхода при операциях ожидания.

    Ключевые слова async и await

    В Python для работы с асинхронностью используются два ключевых слова:

  • async def: Объявляет функцию как асинхронную (корутину). Это сигнал интерпретатору, что выполнение этой функции можно приостанавливать.
  • await: Говорит программе: «Здесь будет долгая операция (например, запрос к БД). Пока мы ждем ответ, иди займись чем-нибудь другим полезным».
  • Пример из нашего кода:

    Используя async def, мы позволяем серверу (Uvicorn) переключаться на обработку других запросов, если внутри этой функции возникнет ожидание (например, при обращении к базе данных).

    Автоматическое тестирование

    «Работает на моей машине» — самая опасная фраза разработчика. Чтобы спать спокойно, нам нужны автотесты. В экосистеме FastAPI стандартом является библиотека pytest.

    Установка зависимостей

    Нам понадобятся pytest для запуска тестов и httpx для выполнения запросов к нашему API внутри тестов.

    TestClient

    FastAPI предоставляет удобный инструмент TestClient, который позволяет отправлять запросы к вашему приложению без запуска реального сервера. Он базируется на библиотеке httpx.

    Создайте файл test_main.py в корне проекта:

    Разберем этот код:

  • Мы импортируем TestClient и наше приложение app.
  • Создаем client. Теперь client — это как бы наш браузер.
  • Функция test_read_main делает GET-запрос к корню сайта.
  • assert (утверждение) проверяет, что статус ответа 200 (OK) и тело ответа совпадает с ожидаемым.
  • Запуск тестов

    В терминале просто выполните команду:

    Pytest автоматически найдет все файлы, начинающиеся на test_, и все функции внутри них, начинающиеся на test_, выполнит их и покажет красивый отчет.

    Тестирование с базой данных

    Тестирование функций, зависящих от базы данных, сложнее. Хорошей практикой является использование отдельной тестовой базы данных, чтобы не засорять основную. В FastAPI это решается через переопределение зависимостей (dependency_overrides).

    Мы можем подменить зависимость get_db на другую, которая будет создавать временную базу данных SQLite в памяти.

    Docker: Контейнеризация приложения

    Теперь, когда код написан и протестирован, нам нужно его запустить на сервере. Раньше это была боль: нужно установить Python, нужную версию библиотек, настроить переменные окружения... И часто версии не совпадали.

    Docker решает эту проблему, упаковывая приложение и все его зависимости (включая саму операционную систему) в контейнер.

    !Структура контейнера, показывающая изоляцию приложения и его зависимостей от основной системы.

    Создание Dockerfile

    Dockerfile — это инструкция по сборке образа вашего приложения. Создайте файл с именем Dockerfile (без расширения) в корне проекта:

    Важные моменты: * FROM python:3.10-slim: Мы берем готовую Linux-систему с уже установленным Python. * COPY . .: Мы переносим файлы с вашего компьютера внутрь образа. * --host 0.0.0.0: Это критически важно для Docker. По умолчанию Uvicorn слушает 127.0.0.1 (localhost), что внутри контейнера означает «только внутри этого контейнера». Чтобы достучаться до приложения снаружи, нужно слушать все интерфейсы (0.0.0.0).

    Сборка и запуск

    Сначала нам нужно создать файл requirements.txt, если его еще нет. Это список всех библиотек:

    Теперь соберем образ (image). Назовем его fastapi-app:

    После успешной сборки запустим контейнер:

    * -d: Detached mode (запуск в фоновом режиме). * -p 8000:80: Проброс портов. Мы говорим: «Все запросы на порт 8000 моего компьютера перенаправляй на порт 80 внутри контейнера».

    Теперь ваше приложение доступно по адресу http://localhost:8000, но работает оно внутри изолированного Linux-окружения.

    Заключение курса

    Поздравляем! Вы прошли путь от новичка до разработчика, способного создать, защитить, протестировать и развернуть современное веб-API.

    Мы изучили:

  • Основы FastAPI: маршрутизацию и структуру.
  • Pydantic: строгую валидацию данных.
  • SQLAlchemy: работу с реляционными базами данных.
  • Security: аутентификацию через JWT.
  • DevOps: тестирование и Docker.
  • FastAPI — это мощный инструмент, который продолжает набирать популярность. Теперь у вас есть прочный фундамент для создания высоконагруженных сервисов, микросервисов и сложных бэкенд-систем. Удачи в разработке!