Друзья, приветствую! Данной статьей я планирую начать большой курс публикаций, в которых я расскажу все, что знаю про FastAPI, начиная от самых простых аспектов и заканчивая более глубокими задачами, такими как JWT авторизация, работа в фоне, валидация данных, правильное подключение базы данных и прочее.

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

Так, к сожалению, не получив должного отклика по теме Docker и Docker Compose, я принял решение не разворачивать глобальную серию публикаций (как это у меня было с Aiogram 3), а ограничиться только коротким и поверхностным упоминанием данной технологии в ряде публикаций. Надеюсь, что с данной серией такого не случится.

Начнем.

Что такое API

Если уже знаете, что такое API, использовали стороннее API в своих проектах или даже писали API самостоятельно, то можете смело пропускать данный раздел и переходить к описанию фреймворка FastAPI. Для тех же, кто не знаком с данной технологией, далее я дам максимально простое объяснение, как из обыденной жизни, так и из мира программирования.

API в повседневной жизни

Скажу так. С API каждый из вас сталкивается регулярно, элементарно, пользуясь Google или Яндекс. Вы отправляете запрос в поисковую строку, и система возвращает вам результаты – это и есть работа API. Оно позволяет различным программам взаимодействовать друг с другом.

API в программировании

В программировании API – это интерфейс, через который различные программные компоненты могут взаимодействовать. Представьте, что у вас есть класс, который отвечает за управление пользователями. API этого класса может включать методы для добавления, удаления и обновления пользователей. Другие части программы будут использовать этот интерфейс для взаимодействия с классом, не вдаваясь в детали его реализации.

К примеру, вам не нужно заходить "под капот" aiogram 3, чтобы использовать функции данного фреймворка, а достаточно только знать, что, к примеру, есть message хендлер, что он принимает определенные параметры и что в его рамках можно делать те или иные действия (например отвечать на ср. Это и есть пример API фреймворка.

API в формате приложений

На уровне приложений API используется для взаимодействия между разными сервисами. Например, ваше приложение может использовать API стороннего сервиса для отправки SMS или получения информации о погоде. Эти API позволяют интегрировать функционал без необходимости писать все с нуля.

На примере тех же телеграмм ботов. Ваш бот может сообщать пользователям, при клике на определенную кнопку, текущий курс рубля по отношению к доллару. Это и будет пример взаимодействия с API. На стороне бота формируется некий запрос. Он, в свою очередь, выполняет отправку на некий сервис, который и возвращает нужные вам данные, а дальше останется просто их отобразить пользователю.

Надеюсь, что у вас больше не возникнет вопросов о том, что такое API.

Введение в FastAPI

Теперь, когда мы разобрались, что такое API, давайте перейдем к нашему главному герою – FastAPI.

FastAPI – это современный, высокопроизводительный фреймворк для создания API на языке Python. Название "FastAPI" подчеркивает его скорость и производительность. Давайте рассмотрим несколько ключевых особенностей, которые делают его таким крутым.

Асинхронность

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

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

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

Работа с HTTP-запросами

FastAPI отлично работает с различными типами HTTP-запросов: GET, POST, PUT, DELETE и другими. Давайте простыми словами обясню, что это такое.

  • GET запросы используются для получения данных с сервера. Например, когда вы заходите на веб-страницу, браузер отправляет GET запрос на сервер, чтобы получить содержимое страницы.

  • POST запросы используются для отправки данных на сервер. Например, когда вы заполняете форму на сайте и нажимаете "Отправить", браузер отправляет POST запрос с данными формы на сервер.

  • PUT запросы используются для обновления данных на сервере. Например, если вы редактируете информацию в профиле пользователя, то запрос на сервер для сохранения изменений будет PUT.

  • DELETE запросы используются для удаления данных на сервере. Например, если вы удаляете запись в своем аккаунте, браузер отправит DELETE запрос на сервер.

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

Разница между front-end и back-end

Прежде чем мы с вами начнем писать код, хотелось бы обратить внимание на один аспект, а именно отличие front-end и back-end. Раскрою простыми словами, чтобы у вас не было вопросов.

Front-end

Front-end – это та часть веб-приложения, с которой взаимодействует пользователь. Это всё, что вы видите и с чем взаимодействуете на веб-странице: кнопки, текстовые поля, изображения, навигация и т.д. Front-end разрабатывается с использованием таких технологий, как HTML, CSS и JavaScript. Он отвечает за отображение данных и обработку пользовательского ввода.

Back-end

Back-end – это "невидимая" часть веб-приложения, которая работает на сервере. Он отвечает за обработку данных, взаимодействие с базой данных, аутентификацию пользователей и другие задачи, которые происходят "за кулисами". Back-end разрабатывается с использованием серверных языков программирования, таких как Python, Java, PHP и других.

FastAPI и его роль

FastAPI – это фреймворк для создания back-end приложений. Он помогает вам создать сервер, который будет обрабатывать запросы от front-end части вашего приложения и возвращать соответствующие ответы. Ваша front-end часть может быть написана на любом языке и с использованием любого фреймворка, и она будет взаимодействовать с back-end, созданным на FastAPI, через API.

Возможности интеграции

Хотя FastAPI в основном предназначен для разработки back-end, его можно использовать и для интеграции с front-end. Вы можете, например, создать API с помощью FastAPI, которое будет взаимодействовать с вашим front-end приложением, предоставляя данные и обрабатывая запросы. Более того, вы можете использовать шаблоны для рендеринга HTML страниц, что позволит вам создавать простые front-end интерфейсы прямо на сервере (Jinja2 нам в помощь).

В итоге, FastAPI – это мощный инструмент для разработки back-end, который легко интегрируется с любым front-end, создавая полный стек вашего веб-приложения.

Задача на сегодня

Сегодня мы начнем писать API некоего университета. Как и в любой университете есть, как студенты, так и преподаватели. Кроме того, имеется расписания, факультеты, курсы и прочее. В данном примере мы все это упусим и будем максимально упрощать.

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

Пример информации по студенту:

{
    "student_id": 1,
    "first_name": "Иван",
    "last_name": "Иванов",
    "date_of_birth": "1998-05-15",
    "email": "ivan.ivanov@example.com",
    "phone_number": "+7 (123) 456-7890",
    "address": "г. Москва, ул. Пушкина, д. 10, кв. 5",
    "enrollment_year": 2017,
    "major": "Информатика",
    "course": 3,
    "special_notes": "Без особых примет"
  }

Создам JSON с именем students.json и помещу в него 10 выдуманных студентов:

[
  {
    "student_id": 1,
    "first_name": "Иван",
    "last_name": "Иванов",
    "date_of_birth": "1998-05-15",
    "email": "ivan.ivanov@example.com",
    "phone_number": "+7 (123) 456-7890",
    "address": "г. Москва, ул. Пушкина, д. 10, кв. 5",
    "enrollment_year": 2017,
    "major": "Информатика",
    "course": 3,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 2,
    "first_name": "Елена",
    "last_name": "Петрова",
    "date_of_birth": "1999-08-20",
    "email": "elena.petrova@example.com",
    "phone_number": "+7 (234) 567-8901",
    "address": "г. Санкт-Петербург, ул. Ленина, д. 5, кв. 8",
    "enrollment_year": 2018,
    "major": "Экономика",
    "course": 2,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 3,
    "first_name": "Алексей",
    "last_name": "Смирнов",
    "date_of_birth": "2000-03-10",
    "email": "alexey.smirnov@example.com",
    "phone_number": "+7 (345) 678-9012",
    "address": "г. Новосибирск, ул. Гагарина, д. 15, кв. 12",
    "enrollment_year": 2019,
    "major": "История",
    "course": 1,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 4,
    "first_name": "Мария",
    "last_name": "Козлова",
    "date_of_birth": "1997-11-25",
    "email": "maria.kozlova@example.com",
    "phone_number": "+7 (456) 789-0123",
    "address": "г. Екатеринбург, ул. Пушкина, д. 20, кв. 3",
    "enrollment_year": 2016,
    "major": "История",
    "course": 4,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 5,
    "first_name": "Дмитрий",
    "last_name": "Соколов",
    "date_of_birth": "1999-04-03",
    "email": "dmitry.sokolov@example.com",
    "phone_number": "+7 (567) 890-1234",
    "address": "г. Казань, ул. Маяковского, д. 25, кв. 7",
    "enrollment_year": 2018,
    "major": "Математика",
    "course": 3,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 6,
    "first_name": "Анна",
    "last_name": "Игнатьева",
    "date_of_birth": "2001-07-18",
    "email": "anna.ignatyeva@example.com",
    "phone_number": "+7 (678) 901-2345",
    "address": "г. Волгоград, ул. Ленина, д. 30, кв. 15",
    "enrollment_year": 2020,
    "major": "Биология",
    "course": 2,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 7,
    "first_name": "Павел",
    "last_name": "Кузнецов",
    "date_of_birth": "1998-09-12",
    "email": "pavel.kuznetsov@example.com",
    "phone_number": "+7 (789) 012-3456",
    "address": "г. Ростов-на-Дону, ул. Гагарина, д. 40, кв. 22",
    "enrollment_year": 2017,
    "major": "Биология",
    "course": 4,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 8,
    "first_name": "Светлана",
    "last_name": "Морозова",
    "date_of_birth": "2000-01-30",
    "email": "svetlana.morozova@example.com",
    "phone_number": "+7 (890) 123-4567",
    "address": "г. Челябинск, ул. Кирова, д. 35, кв. 2",
    "enrollment_year": 2019,
    "major": "Психология",
    "course": 1,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 9,
    "first_name": "Константин",
    "last_name": "Федоров",
    "date_of_birth": "1997-12-05",
    "email": "konstantin.fedorov@example.com",
    "phone_number": "+7 (901) 234-5678",
    "address": "г. Уфа, ул. Советская, д. 50, кв. 10",
    "enrollment_year": 2016,
    "major": "Биология",
    "course": 4,
    "special_notes": "Без особых примет"
  },
  {
    "student_id": 10,
    "first_name": "Ольга",
    "last_name": "Никитина",
    "date_of_birth": "1999-06-20",
    "email": "olga.nikitina@example.com",
    "phone_number": "+7 (012) 345-6789",
    "address": "г. Томск, ул. Ленина, д. 60, кв. 18",
    "enrollment_year": 2018,
    "major": "Экология",
    "course": 3,
    "special_notes": "Без особых примет"
  }
]

Расценивайте данный массив, как таблицу в SQL базе данных (например PostgreSQL).

Дочитав эту статью мы:

  • Создадим 2 простые функции, которые будут взаимодействовать с этим JSON (создавать его и доставать данные)

  • Разберемся со структурой проекта FastApi (скажем так, структура пока не "боевая", но для дмонстрации самое то)

  • Напишем свое первое приложение (создадим первое простое API, которое позволит нам получать данные о студентах)

  • Научимся запускать FastApi приложение

Начинаем писать код

Давай создадим свое первое FastApi приложение! Так как это вводная статья – сегодня я намеренно буду все упрощать и коснемся мы только обработчиков GET запросов.

  1. Создаем проект под FastApi

  • app (папка в которой будем писать код)

  • --- файл main.py

  • -- файл students.json (наш json, который сегодня будет использоваться в качестве мини базы данных

  • -- utils.py (файл с простыми утилитами, к примеру тут будет утилита, которая позвлоит трансформировать данные из json файла в список питоновсских словарей.)

  • -- test.py (функция в которой мы будем тестировать наше API через модуль requests)

  • -- req.txt - файл с зависимостями

Заполняем файл с зависимостями так:

fastapi[all]
requests

Для установки зависимостей воспользуемся командой:

pip install -r req.txt

О том почему необходимо отдельно создавать папку app в корневой дирректории проекта вы узнаете (поймете) в следующих публикациях (если они будут).

fastapi[all] - такую запись мы использовали не случайно.

После выполнения данной команды у нас начнется установка не только FastApi, но и всех модулей входящих в его экосистему. например scarlet, Pydantic и так далее. Это удобно, так как не будет необходимости после каждый модуль устанавливать отдельно.

requests позволит нам тестировать наше API через GET запросы.

Начнем писать код.

Файл utils.py

import json


def dict_list_to_json(dict_list, filename):
    """
    Преобразует список словарей в JSON-строку и сохраняет её в файл.

    :param dict_list: Список словарей
    :param filename: Имя файла для сохранения JSON-строки
    :return: JSON-строка или None в случае ошибки
    """
    try:
        json_str = json.dumps(dict_list, ensure_ascii=False)
        with open(filename, 'w', encoding='utf-8') as file:
            file.write(json_str)
        return json_str
    except (TypeError, ValueError, IOError) as e:
        print(f"Ошибка при преобразовании списка словарей в JSON или записи в файл: {e}")
        return None


def json_to_dict_list(filename):
    """
    Преобразует JSON-строку из файла в список словарей.

    :param filename: Имя файла с JSON-строкой
    :return: Список словарей или None в случае ошибки
    """
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            json_str = file.read()
            dict_list = json.loads(json_str)
        return dict_list
    except (TypeError, ValueError, IOError) as e:
        print(f"Ошибка при чтении JSON из файла или преобразовании в список словарей: {e}")
        return None

Я написал 2 простые функции. Первая принимает список питоновских словарей, создавая JSON файл. А вторая – трансформирует JSON файл в список питоновских словарей. Далее мы просто импортируем эту функцию в приложение FastApi.

Далее мы будем работать с форматом JSON на страницах нашего веб-приложения. Поэтому, для удобства (пользователи Chrome и Yandex Browser) можете поставить плагин JSON Viewer. Для пользователей других браузеров, думаю, тоже найдется что то подходящее.

JSON Viewer

Начинаем писать приложение (файл app/main.py)

Для начала нам необходимо выполнить следующие импорты:

from fastapi import FastAPI
from utils import json_to_dict_list
import os
from typing import Optional

Описание импортов:

  • json_to_dict_list: наша функция, которая будет возвращать всех студентов

  • os: модуль, который поможет нам настроить относительные пути к JSON

  • Optional: позволит нам передавать значения по умолчанию в параметры пути и запросов (подробнее далее)

  • FastAPI: тут, думаю, понятно :)

Пропишем путь к JSON файлу и сохраним в переменной:

import os


# Получаем путь к директории текущего скрипта
script_dir = os.path.dirname(os.path.abspath(__file__))

# Переходим на уровень выше
parent_dir = os.path.dirname(script_dir)

# Получаем путь к JSON
path_to_json = os.path.join(parent_dir, 'students.json')

Сокращенная запись:

path_to_json = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'students.json')

Создадим наше первое приложение:

app = FastAPI()

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

@app.get("/students")
def get_all_students():
    return json_to_dict_list(path_to_json)

Декоратор @app.get("/students"):

  • Этот декоратор используется в FastAPI для обозначения того, что данная функция будет обрабатывать HTTP GET запросы, направленные на маршрут /students.

  • Когда клиент (например, веб-браузер или другой сервис) делает GET запрос по этому маршруту, FastAPI вызывает функцию get_all_students.

Вернет данная функция всех наших студентов, так как никаких филтров мы пока не использовали.

Теперь запустим приложение.

Запуск необходимо выполнять именно с корневой дирректории FastApi проекта, а не из папки app!

Для запуска воспользуемся командой:

uvicorn app.main:app

В результате вы должны получить примерно такой вывод:

Для того чтоб остановить приложение необходимо использовать комбинацию CTRL+C.

Начинаем тестировать.

Для этого просто перейдем по http://127.0.0.1:8000.

: (
: (

Тут мы видим некий ответ и информацию:

{
  "detail": "Not Found"
}

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

@app.get("/")
def home_page():
    return {"message": "Привет, Хабр!"}

Перезапустим наш сервер (CTRL+C) и для удобства предлагаю добавить в запуск флаг –reload, что позволит нам использовать наш сервер в режиме раработки. Любое изменение внесенное в коде автоматически будет применено в проекте без необходимости выполнять перезагрузку.

uvicorn app.main:app --reload

Обновим главную страницу и смотрим:

Видим, что данные отобразились. Конечно, в боевом проекте это была бы красивая главная страница сайта, но до фронта мы пока не дошли.

Теперь давайте протестируем нашу функцию со всеми студентами. Переходим на http://127.0.0.1:8000/students и видим:

Тут информация на 10 студентов. Просто на скрин не влезло.
Тут информация на 10 студентов. Просто на скрин не влезло.

Видим все данные, а это значит, что наш сервер корректно отработал и вернул те данные что мы запросили. В боевом проекте переходом по ссылке http://127.0.0.1:8000/students мы бы выполнили запрос к базе данных и вернули бы информацию на страницу. Пока, как вы уже знаете, в качестве хранилища информации мы используем JSON.

Параметры пути и параметры запросов

Параметры пути

Параметры пути (path parameters) включены в сам маршрут URL и используются для идентификации ресурса.

На примере наших студентов это может быть курс. Будет на практике записываться так:

@app.get("/students/{course}")
def get_all_students_course(course: int):
    students = json_to_dict_list(path_to_json)
    return_list = []
    for student in students:
        if student["course"] == course:
            return_list.append(student)
    return return_list

Обратите внимание на синтаксис. Путь мы указываем в фигурных скобках, давая имя переменной. Далее необходимо в саму функцию передать эту переменную.

После будет происходить следующее. Функция будет принимать путь, тем самым генерируя ссылку по типу /students/2. Далее параметр пути будет автоматически передан в функцию и нам останется только перехватить его и обработать.

В данном случае хорошим примером может послужить пост в блоге. У поста есть некий идентификатор. Мы программируем так, чтоб при нахождении конкретного идентификатора мы получали конкретную статью. Далее происходит такая же логика, как и в примере описанном выше.

Другой хороший пример – это категория на любом сайте.

Важно указать тип данных аргументу, который будет в функции-обработчике. Это необходимо для корректной работы и внутренней валидации. Данному аспекту я планирую посвятить отдельную статью, а точнее модулю Pydantic, входящему в экосистему FastApi.

Перейдем по ссылке http://127.0.0.1:8000/students/2:

Всего 2 студента на втором курсе. Такой себе университет у нас...
Всего 2 студента на втором курсе. Такой себе университет у нас...

Мы видим, что сейчас вернулись только студенты второго курса. Можем попробовать указать в параметр пути 1.

Вывод всех студентов первого курса.
Вывод всех студентов первого курса.

Отлично. На этом простом примере я продемонстрировал как работает параметр пути, но что там с параметрами запроса?

Параметры запроса

В FastAPI параметры запроса (request parameters) - это значения, которые передаются в запросе к API и используются для фильтрации, сортировки или любых других операций над данными.

В ссылке эти параметры прописываются после ?, а если параметров несколько, то разделены они знаком амперсанты (&). Если вы пользовались браузером, то параметрами запроса вы пользовались не один десяток (сотню?) раз.

Для примера давайте рассмотрим наш любимый Хабр.

Там я зайду в поиск и попробую найти по себе информацию:

https://habr.com/ru/search/?q=yakvenalex&target_type=posts&order=relevance

Обратите внимание. Я просто вбил свой логин и нажал на поиск, но в строке сразу сформировался параметры пути (/ru/search/) и сразу 3 параметра запроса:

  • q = yakvenalex

  • target_type = post

  • order = relevance

А теперь давайте мы такое же сделаем с нашими студентами. Сейчас для удобства курс выведу в параметр запроса, а после мы объединим и параметр запроса и параметр пути в одном эндпоинте (функции).

@app.get("/students")
def get_all_students(course: Optional[int] = None):
    students = json_to_dict_list(path_to_json)
    if course is None:
        return students
    else:
        return_list = []
        for student in students:
            if student["course"] == course:
                return_list.append(student)
        return return_list

Давайте разбираться.

Как вы понимаете, я переписал наш стартовый эндпоинт. Теперь он не просто возвращает всех студентов, но позволяет использовать параметры запроса.

Так же вы можете видеть, что я использовал запись course: Optional[int] = None. Благодаря этому мы не только указали, что FastApi должен ждать целое число (int), но и разрешили вообще не передавать этот параметр (параметр запроса).

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

На «боевых» проектах используется более глубокая и детальная валидация данных при помощи Pydantic, но об этом поговорим в следующий раз, если, в целом, тема разработки собственного API через Python вам будет интересна.

Смотрим:

Ссылка теперь имеет вид: http://127.0.0.1:8000/students?course=1. Если бы параметров было несколько, то мы бы их перекликали через &.

Если мы не будем передавать параметров запроса, то получим всех студентов.Если мы не будем передавать параметров запроса, то получим всех студентов.

А теперь давайте рассмотрим пример комбинации параметров пути и параметров запроса.

Пускай в нашем рабочем примере мы будем передавать в параметр пути курс, а в параметры запроса специальность (major) и год поступления (enrollment_year).

@app.get("/students/{course}")
def get_all_students_course(course: int, major: Optional[str] = None, enrollment_year: Optional[int] = 2018):
    students = json_to_dict_list(path_to_json)
    filtered_students = []
    for student in students:
        if student["course"] == course:
            filtered_students.append(student)

    if major:
        filtered_students = [student for student in filtered_students if student['major'].lower() == major.lower()]

    if enrollment_year:
        filtered_students = [student for student in filtered_students if student['enrollment_year'] == enrollment_year]

    return filtered_students

Тут уже необходимо задействовать логику. Я решил данную задачу так.

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

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

Ссылка теперь будет иметь такой вид:

http://127.0.0.1:8000/students/1?enrollment_year=2019&major=Психология

Порядок передачи параметров запроса значения не имеет. Главное не нарушать общий синтаксис.

Смотрим:

Обратите внимание. Несмотря на то, что я вписал русскими символами и с большой буквы в адресной строке мой запрос (Психология) — конечная ссылка изменила свой вид. Тут чисто вопрос в работе браузера. Пусть вас это не пугает.

Тестируем свое апи

Конечно, это все крайне интересно, но вопрос зачем? На него я вам сейчас наглядно отвечу.

Надеюсь, что вы имеете базовое представление про Python библиотеку requests. Если совсем коротко и просто, то основной смысл библиотеки в отправке HTTP-запросов. Это позволяет легко взаимодействовать с веб-сервисами и API. Она упрощает выполнение различных типов запросов, таких как GET и POST, и обработку ответов, обеспечивая удобный и понятный интерфейс для работы с HTTP.

Как вы понимаете, это то что нам нужно, ведь API у нас уже есть (какое никакое).

import requests


def get_all_students():
    url = "http://127.0.0.1:8000/students"
    response = requests.get(url)
    return response.json()


students = get_all_students()
for i in students:
    print(i)

Библиотека requests не является встроенной, так что не забудьте ее установить.

Изначально нам нужна ссылка на которую мы выполним наш запрос. В данном случае это:

http://127.0.0.1:8000/students

Далее мы выполняем сам запрос. В данном случае достаточно только передать ссылку.

А затем вы отправляете запрос к своему серверу, трансформируя ответ в JSON. Основная прелесть тут в том, что мы получаем список питоновских словарей, что позволяет с легкостью дальше работать со своим API.

Смотрим:

Вывод результата в консоль
Вывод результата в консоль

Все четко отработало!

Теперь давайте выполним запрос с параметром запроса (пока параметр пути трогать не будем).

def get_students_with_param_requests(course: int):
    url = "http://127.0.0.1:8000/students"
    response = requests.get(url, params={"course": course})
    return response.json()


students = get_students_with_param_requests(course=2)
for student in students:
    print(student)

На данном примере вы видите, что имеется специальный атрибут, который принимает параметры (params) в виде питоновского словаря.

Сморим:

Получили 2 студента
Получили 2 студента

Нужных нам студентов мы получили, а что там с параметрами пути?

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

def get_students_with_param_path(course: int):
    url = f"http://127.0.0.1:8000/students/{course}"
    response = requests.get(url)
    return response.json()


students = get_students_with_param_path(2)
for student in students:
    print(student)

Обратите внимание. Параметр пути передается в ссылке. Других параметров мы не передали.

Смотрим:

Почему всего 1 студент!?
Почему всего 1 студент!?

Внимательный читатель спросит. А чего это мы получили всего одного студента, когда на втором курсе у нас 2 учится! А тут все просто, мы по умолчанию установили год поступления 2018. А из 2018-го на втором курсе у нас 1 студент.

Миксуем параметры пути и параметры запроса.

def get_students_with_param_mix(course: int, major: str, enrollment_year: int):
    url = f"http://127.0.0.1:8000/students/{course}"
    response = requests.get(url, params={"major": major, "enrollment_year": enrollment_year})
    return response.json()


students = get_students_with_param_mix(2, major=None, enrollment_year=2018)
print(students)

Смотрим:

Распечатал списком, зная что у меня всего 1 такой студент.

Теперь небольшое задание.

Напишите самостоятельно 2 метода, которые будут принимать ID студента и будут возвращать его. Первый метод пусть работает через параметр запроса, а второй через параметр пути.

Я напишу эти функции, но их реализацию, как и полный исходник кода с данной статьи, вы найдете в моей телеграмм канале.

Что там с документацией?

Вы будете удивлены, но документацию FastApi уже написал за нас. Более того, этот фреймворк ещё и песочницу создал с формами для ввода параметров.

Для того чтоб убедиться в этом — необходимо перейти по ссылке: http://127.0.0.1:8000/docs

Не песня ли это? ;)
Не песня ли это? ;)

Каждая функция, которую мы написали, не только тут отобразилась, но и мы получили возможность ее поклацать (песочница). Смотрим.

Тут мы видим запись «No parameters». Не удивительно, ведь сам эндпоинт главной страницы сайта (функция) не принимаете никаких параметров.

По клику на «Try it out» запуск скрипта не происходит, а вы только входите в меня ввода параметров.

Для выполнения самого скрипта необходимо после нажать на «Execute»

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

Не знаю как вас, но меня, при первом знакомстве, данный функционал привел в восторг.

Теперь давайте попробуем выполнить запрос с параметрами.

Мы видим, что сформировалась форма ввода параметров. Кроме того, FastApi подсветил обязательные параметры (звездочка) и добавил комментарий, что это параметр пути (path), кроме того фреймворк указал на параметр, который используется по умолчанию (Default value: 2018) и не установил звездочки возле major, так как это не обязательный параметр.

Выполним запрос.

Тапаем на "Try it out" и вводим параметры запроса.
Тапаем на "Try it out" и вводим параметры запроса.

Обратите внимание. FastApi ещё и сформировал curl запрос. О том что это и как с этим можно работать как то в следующий раз.

Заключение

Наше знакомство с FastAPI подошло к концу. Сегодня мы познакомились с некоторыми возможностями этого фреймворка, однако для создания полноценных проектов этого объема информации недостаточно.

Я специально упростил статью, чтобы сделать материал доступным каждому. Тем не менее, понимание основных концепций API, HTTP-запросов, а также практическое применение FastAPI закладывает прочную основу для разработки вашего собственного API в будущем.

Напомню, что это была пилотная статья. Ваши лайки, подписки, участие в голосовании под этой статье и комментарии помогут понять, насколько этот контент интересен и полезен для вас и, в целом, целесобразность написания подобных статей.

Исходный код и решение задачи с эндпоинтами для получения информации о конкретном студенте вы найдете в моем Telegram-канале.

На этом пока все. Надеюсь, что эта статья станет не последней в цикле моих материалов о FastAPI.

Комментарии (4)


  1. icya
    02.07.2024 15:44
    +2

    from typing import Optional

    Версия 3.10 вышла в конце 2021 года (сейчас 2024), уже можно делать так:

    - def get_all_students(course: Optional[int] = None):
    + def get_all_students(course: int | None = None):

    Тут тоже бы переписать

    if course is None:
        return students
    - else:
    -     return_list = []
    -     for student in students:
    -         if student["course"] == course:
    -             return_list.append(student)
    -     return return_list
    + return_list = []
    + for student in stu dents:
    +     if student["course"] == course:
    +     return_list.append(student)
    + return return_list
    • -- test.py (функция в которой мы будем тестировать наше API через модуль requests)

    Я ожидал (да, понимаю, проблема ожиданий), что будет pytest/unittest/whatever, а по факту это функции, которые в консоль плюют

    Я напишу эти функции, но их реализацию, как и полный исходник кода с данной статьи, вы найдете в моей телеграмм канале

    Может всё таки линк репы на гитхаб/лаб? Сегодня пост в телеге среди последних, а через год-два листать простыню ради этого сообщения, чтобы сравнить код?

    Если уж планируете цикл статей для новичков, пожалуйста, постарайтесь избегать плохих практик. Сомневаюсь, что новички ломанутся изучать бест практис и всё такое. Скорее, они будут именно что повторять за вашим стилем и мы будем иметь что-то из выше озвученного


    1. yakvenalex Автор
      02.07.2024 15:44

      Я не согласен с рядом ваших замечаний.

      - def get_all_students(course: Optional[int] = None):+ def get_all_students(course: int | None = None):

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

      В примере с условием if else я так же намеренно упрощал. Интересно почему тогда вы не сделали замечание по поводу НЕ использования генератора списка?

      Pytest? Я тут описывал что такое апи и гет запросы. Вас это не смутило?)

      Если будет тут поддержка пойдут и "боевые практики" и пайтест и пайдентик, но всем свое время и не делайте заранее выводов на счет моих "устаревших практик". Просто следите за обновлениями)


  1. vladus80
    02.07.2024 15:44
    +1

    Замечательно! Продолжайте делиться опытом, этом у вас очень неплохо получается. Содержательный материал и доходчивая подача


    1. yakvenalex Автор
      02.07.2024 15:44

      Благодарю за обратную связь)