Урок курса
Path-параметры и типы
FastAPI для начинающих: API с базой данных и тестамиВ прошлом уроке мы зарегистрировали маршрут GET /api/rooms и получили список комнат одним декоратором. Теперь добавим второй маршрут, который принимает конкретный идентификатор прямо в URL.
Значение из пути и аннотация int
Когда нужно обратиться к конкретному ресурсу — не ко всему списку, а к одной комнате — принято помещать идентификатор прямо в путь: /api/rooms/3. Чтобы FastAPI понял, что 3 — это переменная, а не фиксированный текст, соответствующий сегмент пути оборачивают в фигурные скобки.
@app.get("/api/rooms/{room_id}")
def get_room(room_id):
return {"room_id": room_id}
Имя внутри скобок — room_id — должно совпадать с именем аргумента функции. Для GET /api/rooms/3 FastAPI извлечёт строку "3" из URL и передаст её в room_id. Именно строку: без аннотации типа никакого преобразования нет, и обработчик вернёт {"room_id": "3"}.
Добавим аннотацию:
@app.get("/api/rooms/{room_id}")
def get_room(room_id: int):
return {"room_id": room_id}
Теперь тот же запрос GET /api/rooms/3 вернёт {"room_id": 3} — уже число. FastAPI прочитал строку "3" из пути, преобразовал её в int и только затем вызвал функцию. Сам Python этого не делает: обычная аннотация типа не превращает аргумент автоматически. Преобразование — работа FastAPI, выполняется до входа в тело функции.
Неверный тип: ответ 422 до обработчика
Раз преобразование происходит до вызова функции, FastAPI может отклонить запрос ещё на входе — если значение не удаётся привести к нужному типу.
Отправьте GET /api/rooms/abc к маршруту с room_id: int. Строка "abc" не превращается в целое число, и функция get_room вообще не запускается. В ответ придёт статус 422 Unprocessable Entity и JSON с описанием проблемы:
{
"detail": [
{
"type": "int_parsing",
"loc": ["path", "room_id"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "abc"
}
]
}
Поле loc показывает источник: "path" — значит, ошибка в сегменте пути, "room_id" — конкретный параметр. Это удобно, когда параметров несколько и нужно быстро найти виновника.
422 отличается от ошибки, которая возникла бы внутри функции. Здесь тело обработчика не выполнялось вовсе: FastAPI остановил запрос до него. Это граница между некорректным входом и логикой приложения.

Поиск по id: существующий ключ и KeyError
Допустим, у нас есть каталог из двух переговорных комнат, хранящийся в словаре с целочисленными ключами:
from fastapi import FastAPI
app = FastAPI()
rooms_by_id = {
1: {"id": 1, "name": "Лондон", "floor": 2},
2: {"id": 2, "name": "Берлин", "floor": 3},
}
@app.get("/api/rooms")
def list_rooms():
return list(rooms_by_id.values())
@app.get("/api/rooms/{room_id}")
def get_room(room_id: int):
return rooms_by_id[room_id]
GET /api/rooms/1 вернёт {"id": 1, "name": "Лондон", "floor": 2}. FastAPI преобразует "1" в 1, функция обращается к словарю по ключу 1 — всё работает.
GET /api/rooms/abc — 422 ещё до словаря, как разобрали выше.
GET /api/rooms/999 — интереснее. Строка "999" успешно преобразуется в int, проверка типа пройдена. Но ключа 999 в rooms_by_id нет. Python выбросит KeyError, FastAPI не поймает его автоматически, и клиент получит 500 Internal Server Error.
Вот принципиальное различие: аннотация int гарантирует только форму значения — что оно целое число. Она ничего не знает о содержимом словаря. Проверка типа и проверка существования записи — два независимых шага. Первый делает FastAPI, второй — ваш код.
Попробуйте решить
Маршрут объявлен как @app.get("/api/rooms/{room_id}") с функцией def get_room(room_id: int). Клиент отправляет запрос GET /api/rooms/5. Какое значение и какого типа получит функция-обработчик в аргументе room_id?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
