FastAPI: создаем высокопроизводительные REST API на Python

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Python-разработчики, заинтересованные в создании REST API
  • Специалисты, стремящиеся повысить свои навыки веб-разработки
  • Люди, изучающие или использующие FastAPI в своих проектах

    FastAPI стремительно захватывает внимание Python-разработчиков, ищущих мощный и быстрый инструмент для создания REST API. Высокая производительность, встроенная валидация данных и автоматическая документация делают этот фреймворк незаменимым для современных веб-приложений. Следуя четкой дорожной карте, вы пройдете от установки FastAPI до разработки полноценного API с аутентификацией и документацией — все это с типизацией и асинхронной обработкой, которые дают ощутимое преимущество в скорости по сравнению с традиционными фреймворками. 🚀

Хотите погрузиться глубже в мир веб-разработки на Python и создавать не только API, но и полноценные веб-сервисы? Обучение Python-разработке от Skypro — это идеальный способ структурировать знания под руководством экспертов. Курс охватывает не только FastAPI, но и все необходимые инструменты современного Python-разработчика, от баз данных до деплоя. Инвестируйте в свои навыки сейчас, чтобы завтра создавать решения корпоративного уровня.

Первые шаги с FastAPI: установка и настройка среды

FastAPI — это современный, высокопроизводительный веб-фреймворк для создания API с Python 3.7+, основанный на стандартных аннотациях типов Python. Перед началом работы необходимо настроить среду разработки и установить необходимые зависимости.

Для начала создадим виртуальное окружение, чтобы изолировать зависимости проекта:

# Создание виртуального окружения
python -m venv venv

# Активация на Windows
venv\Scripts\activate

# Активация на Unix/MacOS
source venv/bin/activate

Теперь установим FastAPI и ASGI-сервер Uvicorn, который будет обрабатывать наши запросы:

pip install fastapi uvicorn

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

Пакет Описание Команда установки
python-multipart Поддержка форм данных pip install python-multipart
SQLAlchemy ORM для работы с базами данных pip install sqlalchemy
pydantic Валидация данных (устанавливается с FastAPI) Включен в зависимости FastAPI
pytest Тестирование API pip install pytest
python-jose Работа с JWT для аутентификации pip install python-jose[cryptography]

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

Python
Скопировать код
# main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

Запустим наше приложение с помощью Uvicorn:

uvicorn main:app --reload

Флаг --reload обеспечивает автоматическую перезагрузку сервера при изменении кода, что удобно для разработки.

Теперь ваш API доступен по адресу http://127.0.0.1:8000, а автоматически сгенерированная документация — по http://127.0.0.1:8000/docs. Эта документация — одно из главных преимуществ FastAPI, она обновляется автоматически при изменении кода. 📚

Александр Петров, Технический директор

Я помню свой первый проект с FastAPI как сейчас. Наша команда получила задание срочно разработать API для нового финтех-сервиса. Ранее мы использовали Flask, но сроки были настолько сжаты, что требовалось найти более эффективное решение. FastAPI оказался именно тем, что нам нужно.

Первый день ушёл только на борьбу с окружением. Мы столкнулись с конфликтами зависимостей на разных машинах разработчиков. Решение пришло неожиданно — Docker-контейнеры стандартизировали окружение и ускорили разработку. Наш Dockerfile был прост:

FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

После этого каждый член команды мог начать работать с кодом без лишних настроек. Скорость разработки выросла на 40%. А встроенная документация FastAPI сэкономила нам недели на написании документации API для фронтенд-команды. Через месяц мы запустили MVP, который успешно выдерживал нагрузку в 3000 запросов в минуту без оптимизации.

Пошаговый план для смены профессии

Создание базовых API-эндпоинтов и обработка запросов

FastAPI делает создание API-эндпоинтов интуитивно понятным благодаря декораторам для различных HTTP-методов. Рассмотрим основные типы запросов и как их обрабатывать.

Начнем с создания структуры проекта для лучшей организации кода:

project/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── routes/
│ │ ├── __init__.py
│ │ └── items.py
│ └── models/
│ ├── __init__.py
│ └── item.py
└── requirements.txt

Теперь реализуем основные типы запросов в файле app/routes/items.py:

Python
Скопировать код
from fastapi import APIRouter, HTTPException, Path, Query
from typing import List, Optional

# Временное хранилище для демонстрации
items_db = {
1: {"name": "Hammer", "price": 9.99, "count": 20, "id": 1, "description": "For hammering nails"},
2: {"name": "Screwdriver", "price": 4.99, "count": 15, "id": 2, "description": "For turning screws"}
}

router = APIRouter(prefix="/items", tags=["items"])

# GET запрос для получения всех элементов
@router.get("/", response_model=List[dict])
async def get_items(skip: int = 0, limit: int = 10):
return list(items_db.values())[skip: skip + limit]

# GET запрос для получения конкретного элемента по ID
@router.get("/{item_id}", response_model=dict)
async def get_item(item_id: int = Path(..., title="ID элемента", ge=1)):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")
return items_db[item_id]

# POST запрос для создания нового элемента
@router.post("/", response_model=dict, status_code=201)
async def create_item(item: dict):
if any(i["name"] == item["name"] for i in items_db.values()):
raise HTTPException(status_code=400, detail="Элемент с таким именем уже существует")

# Генерируем новый ID
new_id = max(items_db.keys(), default=0) + 1
item["id"] = new_id
items_db[new_id] = item
return item

# PUT запрос для обновления существующего элемента
@router.put("/{item_id}", response_model=dict)
async def update_item(item: dict, item_id: int = Path(..., title="ID элемента для обновления", ge=1)):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")

# Обновляем данные, сохраняя ID
item["id"] = item_id
items_db[item_id] = item
return item

# DELETE запрос для удаления элемента
@router.delete("/{item_id}")
async def delete_item(item_id: int = Path(..., title="ID элемента для удаления", ge=1)):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")

deleted_item = items_db.pop(item_id)
return {"message": f"Элемент {deleted_item['name']} удален"}

Теперь интегрируем эти маршруты в наше основное приложение в файле app/main.py:

Python
Скопировать код
from fastapi import FastAPI
from app.routes import items

app = FastAPI(
title="Inventory API",
description="API для управления инвентарем",
version="0.1.0"
)

# Подключаем маршруты
app.include_router(items.router)

@app.get("/")
async def root():
return {"message": "Добро пожаловать в Inventory API! Перейдите на /docs для документации."}

Ключевые особенности обработки запросов в FastAPI:

  • Path Parameters — параметры, встроенные в URL, например {item_id}
  • Query Parameters — параметры, передаваемые в URL после ?, например ?skip=0&limit=10
  • Request Body — тело запроса, обычно в формате JSON
  • Dependency Injection — мощный механизм для переиспользования кода и управления зависимостями
  • Response Models — определяют формат возвращаемых данных и автоматически валидируют их

FastAPI автоматически обрабатывает параметры запроса, преобразуя их в соответствующие типы Python. Если преобразование невозможно или не соответствует указанным ограничениям, API вернет ошибку 422 Unprocessable Entity с подробным описанием проблемы. 🛠️

Валидация данных и типизация с Pydantic в FastAPI

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

Создадим модели данных с валидацией для нашего API в файле app/models/item.py:

Python
Скопировать код
from pydantic import BaseModel, Field, validator
from typing import Optional
import re

class ItemBase(BaseModel):
name: str = Field(..., min_length=1, max_length=50, description="Название товара")
description: Optional[str] = Field(None, max_length=500, description="Описание товара")
price: float = Field(..., gt=0, description="Цена товара (должна быть положительной)")
count: int = Field(..., ge=0, description="Количество товаров на складе")

@validator('name')
def name_must_be_valid(cls, v):
if not re.match(r'^[a-zA-Z0-9 \-_]+$', v):
raise ValueError('Название может содержать только буквы, цифры, пробелы, дефисы и подчеркивания')
return v

@validator('price')
def price_must_be_realistic(cls, v):
if v > 1000000:
raise ValueError('Цена кажется нереалистично высокой')
return round(v, 2) # Округляем до 2 знаков после запятой

class ItemCreate(ItemBase):
# Дополнительные поля или валидации для создания товара
pass

class ItemUpdate(ItemBase):
# Все поля необязательны при обновлении
name: Optional[str] = Field(None, min_length=1, max_length=50)
price: Optional[float] = Field(None, gt=0)
count: Optional[int] = Field(None, ge=0)

class ItemResponse(ItemBase):
id: int = Field(..., description="Уникальный идентификатор товара")

class Config:
orm_mode = True # Позволяет использовать ORM-объекты напрямую

Теперь обновим наши маршруты в app/routes/items.py, чтобы использовать новые модели:

Python
Скопировать код
from fastapi import APIRouter, HTTPException, Path, Query, Depends
from typing import List, Optional
from app.models.item import ItemCreate, ItemUpdate, ItemResponse

# Временное хранилище
items_db = {
1: {"name": "Hammer", "price": 9.99, "count": 20, "id": 1, "description": "For hammering nails"},
2: {"name": "Screwdriver", "price": 4.99, "count": 15, "id": 2, "description": "For turning screws"}
}

router = APIRouter(prefix="/items", tags=["items"])

@router.get("/", response_model=List[ItemResponse])
async def get_items(skip: int = 0, limit: int = 10):
return list(items_db.values())[skip: skip + limit]

@router.get("/{item_id}", response_model=ItemResponse)
async def get_item(item_id: int = Path(..., title="ID элемента", ge=1)):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")
return items_db[item_id]

@router.post("/", response_model=ItemResponse, status_code=201)
async def create_item(item: ItemCreate):
if any(i["name"] == item.name for i in items_db.values()):
raise HTTPException(status_code=400, detail="Элемент с таким именем уже существует")

# Преобразуем Pydantic модель в словарь и добавляем ID
new_id = max(items_db.keys(), default=0) + 1
item_dict = item.dict()
item_dict["id"] = new_id

items_db[new_id] = item_dict
return item_dict

@router.put("/{item_id}", response_model=ItemResponse)
async def update_item(item_id: int = Path(..., ge=1), item_update: ItemUpdate):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")

# Получаем текущий элемент
current_item = items_db[item_id]

# Обновляем только предоставленные поля
update_data = item_update.dict(exclude_unset=True)
updated_item = {**current_item, **update_data}

items_db[item_id] = updated_item
return updated_item

@router.delete("/{item_id}")
async def delete_item(item_id: int = Path(..., ge=1)):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Элемент не найден")

deleted_item = items_db.pop(item_id)
return {"message": f"Элемент {deleted_item['name']} удален"}

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

Функциональность Преимущество Пример использования
Автоматическая валидация Проверка данных до выполнения бизнес-логики price: float = Field(..., gt=0)
Документация API Автоматическая генерация Swagger/OpenAPI Описание полей и ограничений отображается в /docs
Преобразование типов Автоматическое приведение данных к нужному типу "5" → 5 (строка в число) при совместимости
Кастомные валидаторы Сложная бизнес-логика валидации Валидатор name_must_be_valid
Вложенные модели Работа со сложными структурами данных Модели можно вкладывать друг в друга

Для более сложных сценариев валидации Pydantic предлагает дополнительные возможности:

  • Условная валидация — зависящая от значений других полей
  • Root валидаторы — проверяющие модель целиком, а не отдельные поля
  • JSON Schema — генерация схемы для автоматической валидации на клиенте
  • Config класс — настройка поведения модели (например, orm_mode для ORM-объектов)

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

Работа с базой данных и реализация CRUD-операций

В реальных проектах данные обычно хранятся в базе данных, а не в памяти. FastAPI отлично интегрируется с SQLAlchemy — одним из самых популярных ORM для Python. Рассмотрим, как реализовать полноценные CRUD-операции с использованием базы данных.

Сначала установим необходимые зависимости:

pip install sqlalchemy databases psycopg2-binary

Для SQLite можно использовать pip install aiosqlite вместо psycopg2.

Создадим файл app/database.py для настройки подключения:

Python
Скопировать код
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# Для PostgreSQL:
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

Теперь определим модель SQLAlchemy в app/models/db_models.py:

Python
Скопировать код
from sqlalchemy import Column, Integer, String, Float, Text
from app.database import Base

class Item(Base):
__tablename__ = "items"

id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), unique=True, index=True)
description = Column(Text, nullable=True)
price = Column(Float)
count = Column(Integer)

Обновим app/main.py, чтобы создавать таблицы при запуске:

Python
Скопировать код
from fastapi import FastAPI
from app.routes import items
from app.database import engine
from app.models import db_models

# Создаем таблицы
db_models.Base.metadata.create_all(bind=engine)

app = FastAPI(
title="Inventory API",
description="API для управления инвентарем",
version="0.1.0"
)

app.include_router(items.router)

@app.get("/")
async def root():
return {"message": "Добро пожаловать в Inventory API! Перейдите на /docs для документации."}

Теперь обновим наши маршруты в app/routes/items.py для работы с базой данных:

Python
Скопировать код
from fastapi import APIRouter, HTTPException, Path, Query, Depends
from sqlalchemy.orm import Session
from typing import List, Optional

from app.models.item import ItemCreate, ItemUpdate, ItemResponse
from app.models.db_models import Item
from app.database import get_db

router = APIRouter(prefix="/items", tags=["items"])

@router.get("/", response_model=List[ItemResponse])
def get_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = db.query(Item).offset(skip).limit(limit).all()
return items

@router.get("/{item_id}", response_model=ItemResponse)
def get_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if item is None:
raise HTTPException(status_code=404, detail="Элемент не найден")
return item

@router.post("/", response_model=ItemResponse, status_code=201)
def create_item(item: ItemCreate, db: Session = Depends(get_db)):
# Проверка на существующее имя
existing_item = db.query(Item).filter(Item.name == item.name).first()
if existing_item:
raise HTTPException(status_code=400, detail="Элемент с таким именем уже существует")

# Создаем модель SQLAlchemy из Pydantic модели
db_item = Item(**item.dict())

# Добавляем в базу данных
db.add(db_item)
db.commit()
db.refresh(db_item)

return db_item

@router.put("/{item_id}", response_model=ItemResponse)
def update_item(item_id: int, item: ItemUpdate, db: Session = Depends(get_db)):
# Получаем существующий элемент
db_item = db.query(Item).filter(Item.id == item_id).first()
if db_item is None:
raise HTTPException(status_code=404, detail="Элемент не найден")

# Обновляем только предоставленные поля
item_data = item.dict(exclude_unset=True)
for key, value in item_data.items():
setattr(db_item, key, value)

db.commit()
db.refresh(db_item)

return db_item

@router.delete("/{item_id}")
def delete_item(item_id: int, db: Session = Depends(get_db)):
db_item = db.query(Item).filter(Item.id == item_id).first()
if db_item is None:
raise HTTPException(status_code=404, detail="Элемент не найден")

# Сохраняем имя перед удалением
item_name = db_item.name

# Удаляем из базы данных
db.delete(db_item)
db.commit()

return {"message": f"Элемент {item_name} удален"}

Максим Соколов, Ведущий разработчик

Однажды мне поручили модернизировать устаревшую систему управления складом. Ранее она была написана на Django и стала работать медленно при росте количества товаров до 100 000 позиций.

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

Решение пришло неожиданно: я перенёс тяжёлые запросы на уровень базы данных, создав несколько представлений (views) в PostgreSQL. Вместо написания сложной логики в Python, я использовал возможности SQL:

Python
Скопировать код
@router.get("/inventory-status", response_model=List[InventoryStatusReport])
async def get_inventory_status(db: Session = Depends(get_db)):
# Запрос к представлению вместо соединения таблиц в ORM
result = db.execute("SELECT * FROM inventory_status_view").fetchall()
return result

Это решение ускорило выполнение отчётов с 12 секунд до 300 миллисекунд. API стал работать стабильно даже при одновременном доступе 200+ пользователей, а клиент получил возможность экспортировать данные в реальном времени.

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

Важные аспекты работы с базами данных в FastAPI:

  • Dependency Injectiondb: Session = Depends(get_db) автоматически создаёт и закрывает соединение с БД
  • Транзакцииdb.commit() сохраняет изменения, при ошибке транзакция автоматически откатывается
  • Асинхронные операции — для асинхронной работы с БД можно использовать databases вместо стандартного SQLAlchemy
  • Миграции — для управления схемой БД рекомендуется использовать Alembic

Для оптимизации производительности при работе с базами данных:

  • Используйте подходящие индексы для часто запрашиваемых полей
  • Применяйте ленивую загрузку (lazy loading) для связанных объектов
  • Используйте пагинацию для больших наборов данных с параметрами skip и limit
  • Кэшируйте результаты запросов, которые редко меняются

При правильной архитектуре FastAPI позволяет создавать масштабируемые приложения, способные обрабатывать большие объемы данных с высокой производительностью. 💾

Продвинутые возможности: аутентификация и документация API

Безопасность и документация — критически важные аспекты любого профессионального API. FastAPI предлагает мощные инструменты для реализации обоих аспектов.

Начнем с реализации JWT-аутентификации. Сначала установим необходимые пакеты:

pip install python-jose[cryptography] passlib[bcrypt]

Создадим файл app/security.py для функций безопасности:

Python
Скопировать код
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
from typing import Optional

# Настройки безопасности
SECRET_KEY = "YOUR_SECRET_KEY" # В реальных проектах используйте переменные окружения
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Модели для аутентификации
class Token(BaseModel):
access_token: str
token_type: str

class TokenData(BaseModel):
username: Optional[str] = None

class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None

class UserInDB(User):
hashed_password: str

# Имитация БД пользователей (в реальном приложении используйте базу данных)
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "john@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}

# Инициализация контекста для хэширования паролей
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
return pwd_context.hash(password)

def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Невозможно проверить учетные данные",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user

async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Неактивный пользователь")
return current_user

Теперь создадим эндпоинты для аутентификации в app/routes/auth.py:

Python
Скопировать код
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from datetime import timedelta

from app.security import (
Token, User, authenticate_user, create_access_token, 
get_current_active_user, ACCESS_TOKEN_EXPIRE_MINUTES, fake_users_db
)

router = APIRouter(tags=["authentication"])

@router.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Неверное имя пользователя или пароль",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}

@router.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user

Добавим эти маршруты в app/main.py:

Python
Скопировать код
from fastapi import FastAPI
from app.routes import items, auth
from app.database import engine
from app.models import db_models

db_models.Base.metadata.create_all(bind=engine)

app = FastAPI(
title="Inventory API",
description="API для управления инвентарем с поддержкой аутентификации",
version="0.1.0",
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json"
)

app.include_router(auth.router)
app.include_router(items.router)

@app.get("/")
async def root():
return {"message": "Добро пожаловать в Inventory API! Перейдите на /docs для документации."}

Теперь защитим доступ к нашим эндпоинтам, требуя аутентификацию. Обновим app/routes/items.py:

Python
Скопировать код
from fastapi import APIRouter, HTTPException, Path, Query, Depends
from sqlalchemy.orm import Session
from typing import List, Optional

from app.models.item import ItemCreate, ItemUpdate, ItemResponse
from app.models.db_models import Item
from app.database import get_db
from app.security import get_current_active_user, User

router = APIRouter(prefix="/items", tags=["items"])

@router.get("/", response_model=List[ItemResponse])
def get_items(
skip: int = 0, 
limit: int = 100, 
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
items = db.query(Item).offset(skip).limit(limit).all()
return items

# Остальные методы также обновляются с добавлением:
# current_user: User = Depends(get_current_active_user)

Для улучшения документации FastAPI используем дополнительные метаданные:

Python
Скопировать код
from fastapi import APIRouter, HTTPException, Path, Query, Depends, Body
from sqlalchemy.orm import Session
from typing import List, Optional

from app.models.item import ItemCreate, ItemUpdate, ItemResponse
from app.models.db_models import Item
from app.database import get_db
from app.security import get_current_active_user, User

router = APIRouter(
prefix="/items", 
tags=["items"],
responses={
404: {"description": "Элемент не найден"},
400: {"description": "Некорректный запрос"},
401: {"description": "Не авторизован"}
}
)

@router.get(
"/", 
response_model=List[ItemResponse],
summary="Получить список всех элементов",
description="""
Получает список элементов инвентаря с возможностью пагинации.

- **skip**: пропустить указанное количество элементов
- **limit**: максимальное количество элементов в ответе
""",
response_description="Список элементов инвентаря"
)
def get_items(
skip: int = Query(0, description="Сколько элементов пропустить"),
limit: int = Query(100, description="Максимальное количество элементов", le=1000),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
items = db.query(Item).offset(skip).limit(limit).all()
return items

# Аналогично обновляем остальные методы с более подробной документацией

Ключевые особенности документации FastAPI:

  • Автоматическая генерация на основе аннотаций типов Python
  • Поддержка Swagger UI (/docs) и ReDoc (/redoc)
  • Возможность описания различных сценариев использования API
  • Настройка примеров запросов и ответов
  • Документирование схем безопасности и требований аутентификации
  • Возможность экспорта документации в форматах JSON или YAML

Для обеспечения безопасности в продакшен-окружении следуйте этим рекомендациям:

  • Используйте переменные окружения для хранения секретных ключей и паролей
  • Добавьте ограничение количества запросов (rate limiting) для предотвращения DoS-атак
  • Настройте CORS (Cross-Origin Resource Sharing) для контроля доступа с разных доменов
  • Регулярно обновляйте зависимости для устранения известных уязвимостей
  • Используйте HTTPS для шифрования данных при передаче

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

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

Загрузка...