
FastAPI 6. Пользовательский сервис и маршруты регистрации
В этой статье мы напишем пользовательский сервис для работы с пользователем. Реализуем и проверим работу маршрута регистрации.
Реклама

Мы подошли к одной из ключевых тем — маршрутам. Чтобы обращаться к API и выполнять нужные действия, необходимо настроить маршруты. Но сами маршруты, по сути, лишь принимают запросы. Вся бизнес-логика должна быть вынесена в специализированный компонент — пользовательский сервис.
Что такое пользовательский сервис?
Пользовательский сервис можно сравнить с менеджером, но у них разные роли:
- Менеджер отвечает за взаимодействие с базой данных.
- Сервис выступает промежуточным звеном между маршрутом и менеджером.
Основная задача сервиса — содержать всю логику, которая выполняется при вызове маршрута. Таким образом, маршрут просто получает запрос, передаёт данные в сервис и возвращает результат. Этот подход позволяет сделать код более структурированным и тестируемым. Хотя иногда логика может быть немного другой, но такой стиль — это хороший базис.
Создание пользовательского сервиса
Для начала создадим файл services.py
в пакете auth
. Класс UserService
будет отвечать за реализацию функционала, связанного с пользователями.
Конструктор класса
Начнём с определения конструктора. Создадим метод __init__
, который принимает два аргумента:
manager
— объект классаUserManager
, отвечающего за работу с базой данных.handler
— объект классаAuthHandler
, выполняющего задачи, связанные с авторизацией.
Оба аргумента будут передаваться через зависимости FastAPI с помощью Depends
. Это упрощает процесс инъекции зависимостей и делает сервис более гибким.
Вот как выглядит конструктор класса:
def __init__(
self,
manager: UserManager = Depends(UserManager),
auth: AuthHandler = Depends(AuthHandler),
) -> None:
self.manager = manager
self.auth = auth
Метод register_user
Теперь добавим в сервис метод для регистрации нового пользователя.
Этот метод:
- Будет асинхронным.
- Примет объект
user
класса схемыRegisterUser
. - Вернёт объект класса схемы
UserReturnData
.
Логика метода:
- Получить хэш пароля пользователя с помощью метода
.get_password_hash
из объектаhandler
. - Создать объект пользователя
new_user
с хэшированным паролем и email. - Передать этого пользователя в метод
.create_user
менеджера для сохранения в базе данных. - Вернуть результат.
Пример реализации:
async def register_user(self, user: RegisterUser) -> UserReturnData:
# Хэшируем пароль
hashed_password = await self.handler.get_password_hash(user.password)
# Создаём объект пользователя
new_user = CreateUser(email=user.email, hashed_password=hashed_password)
# Сохраняем пользователя в базе и возвращаем результат
return await self.manager.create_user(user=new_user)
Полный код файла:
from fastapi import Depends
from lkeep.apps.auth.handlers import AuthHandler
from lkeep.apps.auth.managers import UserManager
from lkeep.apps.auth.schemas import RegisterUser, UserReturnData, CreateUser
class UserService:
def __init__(self, manager: UserManager = Depends(UserManager), handler: AuthHandler = Depends(AuthHandler)):
self.manager = manager
self.handler = handler
async def register_user(self, user: RegisterUser) -> UserReturnData:
hashed_password = await self.handler.get_password_hash(user.password)
new_user = CreateUser(email=user.email, hashed_password=hashed_password)
return await self.manager.create_user(user=new_user)
Этот подход позволяет разгрузить маршруты и сделать логику более модульной. Сервис можно легко тестировать, подменяя зависимости моками, а маршруты остаются максимально простыми.
Маршрут регистрации
Маршруты, они же эндпоинты (endpoints) или "ручки", — это пути, по которым клиент может обращаться к вашему API. Например, site.ru/api/v1/get_users
. Каждый маршрут также определяет метод HTTP-запроса: GET
, POST
, PUT
, DELETE
и другие. Если вы не знакомы с HTTP-методами, настоятельно рекомендую прочитать об этом перед тем, как двигаться дальше, например, в статье на Хабр.
Создание маршрута регистрации
Шаг 1. Создаём роутер
В пакете auth
создадим файл routes.py
. Этот файл будет содержать маршруты, относящиеся только к приложению аутентификации.
Внутри файла создадим переменную auth_router
, которая будет экземпляром класса APIRouter
. Это специальный инструмент FastAPI, предназначенный для группировки маршрутов.
Укажем следующие параметры при создании роутера:
prefix
: общий префикс для всех маршрутов в роутере (например,/auth
).tags
: список тегов, помогающий группировать маршруты в документации Swagger.
Пример кода:
from fastapi import APIRouter
auth_router = APIRouter(prefix="/auth", tags=["auth"])
Важно: при указании префикса (prefix
) обязательно добавляйте /
в начале строки! Это требование FastAPI.
Шаг 2. Создаём функцию-обработчик для маршрута
Добавим асинхронную функцию registration
, которая будет обрабатывать запросы на регистрацию новых пользователей. Она принимает:
user
: объект схемыRegisterUser
, представляющий данные, которые отправляет клиент.service
: экземплярUserService
, передаваемый через зависимость (Depends
).
Внутри функции вызываем метод register_user
у объекта service
, передавая ему данные пользователя. Результат выполнения возвращается в ответ.
Пример кода:
from fastapi import Depends
from lkeep.apps.auth.schemas import RegisterUser, UserReturnData
from lkeep.apps.auth.services import UserService
async def registration(user: RegisterUser, service: UserService = Depends(UserService)) -> UserReturnData:
return await service.register_user(user=user)
Шаг 3. Добавляем маршрут в роутер
Чтобы функция-обработчик стала полноценным маршрутом, нужно использовать декоратор, добавляющий её в роутер. Для этого обращаемся к методу .post
объекта auth_router
.
Передаём в декоратор следующие параметры:
path
: путь маршрута относительно префикса (в данном случае"/register"
).response_model
: схема данных, возвращаемая в ответе.status_code
: HTTP-статус ответа. Здесь указан201 CREATED
, который используется для подтверждения успешного создания ресурса.
Итоговый код функции с декоратором:
from starlette import status
@auth_router.post(
"/register",
response_model=UserReturnData,
status_code=status.HTTP_201_CREATED
)
async def registration(
user: RegisterUser,
service: UserService = Depends(UserService)
) -> UserReturnData:
return await service.register_user(user=user)
Полный код файла routes.py
Вот как выглядит полный файл с маршрутом регистрации:
from fastapi import APIRouter, Depends
from starlette import status
from lkeep.apps.auth.schemas import RegisterUser, UserReturnData
from lkeep.apps.auth.services import UserService
auth_router = APIRouter(prefix="/auth", tags=["auth"])
@auth_router.post(
"/register",
response_model=UserReturnData,
status_code=status.HTTP_201_CREATED
)
async def registration(
user: RegisterUser,
service: UserService = Depends(UserService)
) -> UserReturnData:
return await service.register_user(user=user)
Что делает этот код?
- Роутер
auth_router
: отвечает за группировку всех маршрутов, относящихся к аутентификации. - Маршрут
POST /auth/register
:- Получает данные пользователя.
- Передаёт их в
UserService
для обработки. - Возвращает результат в виде объекта
UserReturnData
.
- Swagger-документация: благодаря
tags
иresponse_model
, маршрут автоматически отображается в Swagger с понятным описанием.
Роутер приложений
На предыдущем шаге мы создали роутер auth_router
для маршрутов, связанных с аутентификацией. Теперь нам нужен общий роутер, который объединяет все маршруты разных приложений в проекте.
Создаём общий роутер
Зачем нужен общий роутер?
Общий роутер позволяет централизованно управлять всеми маршрутами вашего проекта. Например:
- Устанавливать общий префикс (
/api/v1
), чтобы все маршруты проекта начинались с него. - Упрощать интеграцию новых модулей (например,
orders
,products
и т.д.). - Обеспечивать совместимость разных версий API (например,
/api/v1
для старой версии и/api/v2
для новой).
Это особенно важно в крупных или развивающихся проектах, где маршруты могут меняться или расширяться.
Добавляем общий роутер
В файле __init__.py
пакета apps
создадим общий роутер apps_router
. Этот роутер будет включать в себя роутеры всех приложений.
Код:
from fastapi import APIRouter
apps_router = APIRouter(prefix="/api/v1")
Почему v1
?
Использование версии API (v1
) не обязательно, но это хороший стандарт. Если в будущем вы внесёте серьёзные изменения в API, вы сможете создать новую версию (/api/v2
), сохранив старую для обратной совместимости. Это особенно важно для публичных API.
Подключаем роутер приложения auth
Чтобы включить маршруты auth_router
в общий роутер, используем метод .include_router
. Вызов выглядит так:
apps_router.include_router(router=auth_router)
Здесь:
router
— это аргумент, который указывает на роутер, который мы подключаем (в данном случае,auth_router
).- Теперь все маршруты из
auth_router
будут доступны в рамкахapps_router
.
Полный код файла __init__.py
Файл __init__.py
пакета apps
будет выглядеть так:
from fastapi import APIRouter
from lkeep.apps.auth.routes import auth_router
apps_router = APIRouter(prefix="/api/v1")
# Подключаем маршруты приложения auth
apps_router.include_router(router=auth_router)
Как работает этот код?
- Создаётся роутер
apps_router
: с общим префиксом/api/v1
. - Подключаются маршруты приложения
auth
: через метод.include_router
. - Объединение маршрутов: Теперь маршруты приложения
auth
будут доступны с полным путём/api/v1/auth
, где:/api/v1
— префикс общего роутера./auth
— префикс роутера приложенияauth
.
Почему это важно?
- Удобство: Все маршруты приложений централизованно собираются в одном месте.
- Гибкость: Легко добавить новые модули, просто подключив их роутеры.
- Масштабируемость: Если проект вырастет, вы сможете поддерживать несколько версий API без лишних проблем.
Главный роутер
Последний шаг — подключить роутер из пакета apps
в главный файл проекта.
Вносим изменения в main.py
Откроем файл main.py
для редактирования.
В файле main.py
уже есть переменная app
, которая является экземпляром класса FastAPI
. Чтобы зарегистрировать роутеры, используем метод include_router
:
app.include_router(router=apps_router)
Здесь:
router
— аргумент, указывающий на объект роутера (apps_router
), который объединяет маршруты всех приложений.
Полный код файла main.py
import uvicorn
from fastapi import FastAPI
from lkeep.apps import apps_router
app = FastAPI()
app.include_router(router=apps_router)
def start():
uvicorn.run(app="lkeep.main:app", reload=True)
Теперь маршруты из всех подключённых приложений будут доступны через ваш API.
Запуск!
Чтобы запустить приложение, выполните команду:
poetry run app
После запуска веб-сервера откройте Swagger по адресу: http://127.0.0.1:8000/docs.
Тестирование маршрута регистрации
Перейдите к маршруту /auth/register
в Swagger-документации.

Нажмите кнопку "Try it out".

Введите данные пользователя:
email
— например,test@example.com
.password
— любой желаемый пароль.

Нажмите "Execute".
После успешного выполнения вы увидите:
- Ответ сервера: информацию о созданном пользователе.
- База данных: новая запись с введённым
email
и хешированным паролем.

Заключение
На этом базовая настройка приложения завершена. Однако это только начало пути. Впереди нас ждут новые вызовы:
- Реализация авторизации через токены.
- Настройка сброса пароля.
- Подтверждение регистрации через email.
- Основной функционал приложения.
Это сложный, но невероятно увлекательный процесс, который поможет вам вырасти как разработчику.
Все статьи