Простое API на FastAPI за 20 минут: пошаговая инструкция
Собираем рабочее REST API с нуля на FastAPI: от установки до первого POST-запроса с валидацией. По шагам, с проверками и типичными ошибками — без предварительного опыта с фреймворком.

Что получится и что понадобится
К концу этих двадцати минут у вас будет работающее API с тремя эндпоинтами: получение списка задач, добавление новой и получение задачи по идентификатору. Данные будут храниться в памяти — без базы данных и миграций. Этого достаточно, чтобы понять конвенции FastAPI и сразу увидеть результат в автоматически сгенерированной документации Swagger UI.
Инструкция написана для FastAPI 0.110+ и Python 3.11+. На более ранних версиях Python (3.9–3.10) всё работает, но синтаксис аннотаций типов немного отличается — где это важно, укажу отдельно.
Перед стартом убедитесь, что есть
- Python 3.11 или новее — проверьте командой
python --version(илиpython3 --versionна macOS/Linux). - pip — обычно идёт вместе с Python. Проверка:
pip --version. - Терминал и любой текстовый редактор. VS Code удобен, но не обязателен.
- Свободный порт 8000 на localhost — его по умолчанию займёт сервер.
Шаг 1. Создаём окружение и ставим зависимости
Работайте в виртуальном окружении — так зависимости проекта не пересекутся с системными пакетами.
# Создаём папку проекта и переходим в неё
mkdir fastapi-todo && cd fastapi-todo
# Виртуальное окружение
python -m venv .venv
# Активация (Linux / macOS)
source .venv/bin/activate
# Активация (Windows, PowerShell)
# .venv\Scripts\Activate.ps1
# Устанавливаем FastAPI и Uvicorn — ASGI-сервер для запуска
pip install "fastapi[standard]"
Проверка: команда pip show fastapi должна показать версию 0.110 или выше. Пакет uvicorn установится автоматически как часть группы зависимостей standard.
Сноска:
ASGI (Asynchronous Server Gateway Interface) — стандарт взаимодействия между Python-приложением и веб-сервером, поддерживающий асинхронные запросы. Uvicorn — одна из распространённых реализаций ASGI-сервера.
Шаг 2. Минимальное приложение — один GET-эндпоинт
Создайте файл main.py в корне проекта:
from fastapi import FastAPI
app = FastAPI(title="TODO API", version="0.1.0")
@app.get("/healthcheck")
def healthcheck():
"""Проверка работоспособности сервиса."""
return {"status": "ok"}
Запустите сервер:
uvicorn main:app --reload
Флаг --reload перезапускает сервер при каждом сохранении файла — удобно при разработке, но в продакшене не используется.
Проверка: откройте в браузере http://127.0.0.1:8000/healthcheck. Ответ — {"status": "ok"}. Теперь откройте http://127.0.0.1:8000/docs — вы увидите интерактивную документацию Swagger UI с одним эндпоинтом.
Шаг 3. Описываем модель данных через Pydantic
FastAPI использует Pydantic для валидации входных данных и сериализации ответов. Вместо ручного разбора JSON вы описываете схему один раз, а фреймворк проверяет типы и формирует понятные ошибки автоматически.
Добавьте в main.py модели и хранилище:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="TODO API", version="0.1.0")
# --- Модели ---
class TaskCreate(BaseModel):
"""Схема для создания задачи."""
title: str
description: str = "" # необязательное поле, по умолчанию пустая строка
done: bool = False
class Task(TaskCreate):
"""Схема задачи с идентификатором — для ответа клиенту."""
id: int
# --- Простое хранилище в памяти ---
tasks: dict[int, Task] = {}
next_id: int = 1
Если вы на Python 3.9 или 3.10, замените dict[int, Task] на Dict[int, Task] из модуля typing.
Шаг 4. Три эндпоинта — CRUD без базы данных
Допишите в тот же main.py:
@app.get("/healthcheck")
def healthcheck():
return {"status": "ok"}
@app.post("/tasks", response_model=Task, status_code=201)
def create_task(payload: TaskCreate):
"""Создаёт новую задачу и возвращает её с присвоенным id."""
global next_id
task = Task(id=next_id, **payload.model_dump())
tasks[next_id] = task
next_id += 1
return task
@app.get("/tasks", response_model=list[Task])
def list_tasks():
"""Возвращает все задачи."""
return list(tasks.values())
@app.get("/tasks/{task_id}", response_model=Task)
def get_task(task_id: int):
"""Возвращает задачу по id. Если не найдена — 404."""
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Задача не найдена")
return tasks[task_id]
Проверка: сервер с --reload подхватит изменения сам. Перейдите в /docs, разверните POST /tasks, нажмите «Try it out» и отправьте:
{
"title": "Купить молоко",
"description": "2.5%, два пакета"
}
Ответ придёт со статусом 201 и полем id: 1. Потом GET /tasks вернёт массив с этой задачей.
Шаг 5. Проверяем валидацию
Попробуйте отправить POST /tasks с телом {"title": 123}. FastAPI не упадёт — Pydantic приведёт число к строке. А вот если убрать поле title совсем, API ответит 422 Unprocessable Entity с подробным описанием ошибки: какое поле пропущено, какой тип ожидался. Вам не пришлось писать для этого ни строчки — всё следует из модели TaskCreate.
Что здесь происходит на самом деле
Полезно понимать три вещи, даже если вы не собираетесь углубляться прямо сейчас:
| Механизм | Что делает | Где это видно |
|---|---|---|
| Аннотации типов | FastAPI читает типы аргументов функции и на их основе строит валидацию и документацию | Параметр payload: TaskCreate — фреймворк знает, что тело запроса должно соответствовать схеме |
response_model | Определяет, какие поля попадут в ответ и в каком формате | Если в будущем вы добавите в объект Task поле internal_note, но не включите его в response_model, клиент его не увидит |
| Автодокументация | OpenAPI-схема генерируется из кода. Swagger UI (/docs) и ReDoc (/redoc) берут её и отрисовывают интерфейс | Откройте /redoc — другой формат той же документации, без кнопки «Try it out» |
Если не получилось
Пять ошибок, которые встречаются чаще всего при первом запуске:
ModuleNotFoundError: No module named 'fastapi'— вы запускаетеuvicornвне виртуального окружения. Активируйте его заново и проверьтеwhich python(Linux/macOS) илиwhere python(Windows) — путь должен вести внутрь.venv.ERROR: [Errno 98] Address already in use(Linux) илиErrno 10048(Windows) — порт 8000 занят. Запустите на другом:uvicorn main:app --reload --port 8001.ImportError: cannot import name 'model_dump' from ...— у вас Pydantic версии 1.x, а методmodel_dump()появился в Pydantic 2. Обновите:pip install --upgrade pydantic. Либо заменитеpayload.model_dump()наpayload.dict()— это аналог для v1.- Ответ 422 при POST — вы забыли заголовок
Content-Type: application/jsonили отправили невалидный JSON. Через Swagger UI (/docs) это не произойдёт, но при тестировании через curl добавьте-H "Content-Type: application/json". - Сервер запустился, но
/docsотдаёт пустую страницу — проверьте консоль браузера. Причиной может быть отсутствие доступа к внешним JS-ресурсам Swagger UI, которые по умолчанию загружаются с CDN. В таком случае подключите статические файлы Swagger UI локально — в документации FastAPI описан параметрdocs_urlи способ раздачи файлов из пакета.
Полный файл main.py
Для удобства — весь код в одном месте, готовый к копированию:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="TODO API", version="0.1.0")
class TaskCreate(BaseModel):
title: str
description: str = ""
done: bool = False
class Task(TaskCreate):
id: int
tasks: dict[int, Task] = {}
next_id: int = 1
@app.get("/healthcheck")
def healthcheck():
return {"status": "ok"}
@app.post("/tasks", response_model=Task, status_code=201)
def create_task(payload: TaskCreate):
global next_id
task = Task(id=next_id, **payload.model_dump())
tasks[next_id] = task
next_id += 1
return task
@app.get("/tasks", response_model=list[Task])
def list_tasks():
return list(tasks.values())
@app.get("/tasks/{task_id}", response_model=Task)
def get_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Задача не найдена")
return tasks[task_id]
Что делать дальше
Рабочее API в одном файле — это отправная точка, а не архитектура. Вот конкретные следующие шаги:
- Подключить базу данных. Хранилище в памяти обнулится при перезапуске. SQLite через SQLAlchemy или Tortoise ORM — минимальный путь к персистентности. В документации FastAPI есть раздел «SQL (Relational) Databases» с готовым примером.
- Добавить PUT и DELETE. Вы уже видите паттерн — новый декоратор, модель, одна функция. Попробуйте сами, прежде чем читать гайд.
- Вынести роутеры. Когда эндпоинтов станет больше пяти, используйте
APIRouter— это аналог blueprints в Flask. Каждый ресурс живёт в своём файле. - Написать тесты. FastAPI поставляется с
TestClient(обёртка над httpx). Один тест на каждый эндпоинт — и рефакторинг перестанет быть страшным.
FastAPI не заставляет выбирать между «быстро написать» и «правильно спроектировать». Вы начинаете с одного файла и наращиваете структуру по мере роста — типы и автодокументация работают на любом масштабе.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: FastAPI — официальная документация (Tutorial - First Steps), Pydantic V2 — официальная документация
Частые вопросы
Обязательно ли использовать Uvicorn? Можно запустить FastAPI на другом сервере?
Данные исчезают после перезапуска сервера. Это нормально?
Чем FastAPI отличается от Flask, если я уже знаю Flask?
Код из статьи не работает на Python 3.9 — ошибка в аннотации dict[int, Task]. Что делать?
Можно ли использовать этот код в продакшене как есть?
Что, если на порту 8000 уже работает другое приложение?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.