FastAPI — как датаклассы на максималках упрощают веб-разработку? | OTUS

FastAPI — как датаклассы на максималках упрощают веб-разработку?

В эпоху реактивных фреймворков, таких как React, Vue.js, Angular и SVELTE, бекенд разработка смещается в сторону API — нам достаточно сделать CRUD-ы, настроить валидацию данных и (опционально) сделать авторизацию пользователя. В таком случае фронтенд-разработка максимально отделена от бекенд-части, тот же рендеринг шаблонов ложится на браузер пользователя.

Для создания RESTful ресурсов есть дополнения для популярных библиотек. Например, для Flask можно воспользоваться Flask-RESTful, или более близкой к сегодняшней теме библиотекой Flask-REST-JSONAPI (эта реализация устарела и не поддерживается, есть доработанный форк с расширяющим возможности дополнением).

Для Django есть Django REST framework. Все эти и другие подобные решения отлично подходят для изменения курса разработки существующего проекта. Но что, если прямо сейчас необходимо начать проект с нуля, вы точно знаете, что нужно будет разрабатывать только API, а ещё хочется чего-то нового? Вот тут на сцене появляется FastAPI! Чем же примечателен этот фреймворк?

Ключевые преимущества можно прочитать на главной странице сайта, но главное, что нас сегодня интересует, это то, как здесь реализована работа с аннотациями типов. Если вы знакомы с датаклассами или библиотекой attrs, то уже понимаете суть pydantic — библиотеки, на которой и основан FastAPI. Давайте же посмотрим, как это работает (пропустим официальный Hello World, он ничем не отличается от того же Flask).

Параметры в url

Похожим на Flask/Django образом указываем в пути переменную. Но делаем это в фигурных скобках, а тип объявляем в функции, которая будет обрабатывать запрос.

from fastapi import FastAPI

app = FastAPI()


@app.get("/user/{user_id}/")
def get_user(user_id: int):
    return {"user_id": user_id}

Также необходимо установить uvicorn для запуска. Пишем в терминале uvicorn main:app --reload и можем перейти на http://127.0.0.1:8000/docs/, где нас поприветствует автоматически сгенерированная Swagger документация:

Автоматически сгенерированная документация Swagger:

1.docs-20219-cfe5ef.png

Если хотите запускать не из терминала, а напрямую в коде, то достаточно импортировать uvicorn и добавить в конце файла строчку uvicorn.run(app).

Здесь мы можем в удобном виде тестировать созданные ресурсы. Но что же насчёт валидации? При попытке перейти на http://127.0.0.1:8000/user/spam/ мы получим такую ошибку:

{
  "detail": [
    {
      "loc": [
        "path",
        "user_id"
      ],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ]
}

Библиотека выполнила валидацию за нас, поэтому мы можем быть уверены, что в функцию нам придёт именно int!

Валидация объектов

Теперь взглянем на более сложные запросы. Например, POST-запрос с передачей данных. Объявим модели с нужными нам полями:

class UserBase(BaseModel):
    """
    Базовая модель пользователя.
    Задавать полное имя не обязательно
    """
    nickname: str
    full_name: str = None


class UserIn(UserBase):
    """
    Описываем, какие данные ожидаем получить на вход
    при создании пользователя. Тут будут унаследованы все поля
    """


class UserOut(UserBase):
    """
    То, как будет выглядеть возвращаемый объект.
    Добавляем поле `id`, оно будет "присвоено" при "сохранении"
    """
    id: int

Теперь объявляем роут. Там проставляем айди-объекта в результат (необходимое поле для возврата на фронт):

@app.post("/user/", response_model=UserOut)
def create_user(user_in: UserIn):
    user_out = UserOut(**user_in.dict(), id=42)
    return user_out

Можем перейти на страницу документации и посмотреть спецификацию. Тут мы видим и какие данные нужно прислать на сервер, и какие будут возвращены клиенту:

Спецификация POST-запроса:

2.docs.post-20219-e03256.png

При запросе данные будут провалидированы, поэтому в переменной user_in будет та самая модель, которую мы ожидаем, и с нужными нам полями.

Теперь выполняем POST-запрос. В редакторе (на странице документации) можем для проверки убрать необязательное поле, поэтому передаём следующее:

{
  "nickname": "OTUS"
}

В ответ нам приходит такой JSON:

{
  "nickname": "OTUS",
  "full_name": null,
  "id": 42
}

Здесь мы видим, что всё пришло, как мы и ожидали. FastAPI выполнит валидацию полей "под капотом", а нам нужно заниматься только логикой приложения.

И это только самая верхушка возможностей валидации. Если вы были заинтересованы, то загляните в User Guide и Advanced User Guide — будете приятно удивлены количеством всего, что поможет упростить разработку! А вот, например, готовый шаблон full-stack проекта.

Кстати, ещё один пункт, как библиотека ускоряет разработку помимо того, что занимается валидацией данных за вас: благодаря pydantic и аннотациям типов ваша среда разработки будет подсказывать все свойства и методы объектов, с которыми вы работаете, а также подсвечивать ошибки, которые так легко допустить, изменив возвращаемые из функции данные, или просто опечатавшись.

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
1 комментарий
0

Реакт не реактивный, как это ни странно.

Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто