Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Best practices
best_practices

Best practices

Итоговые рекомендации, чек-листы, антипаттерны

Открыть лабораториюv1.0Запускается локально из публичного репозитория

Best Practices для FastAPI

Итоговые рекомендации, чек-листы, антипаттерны.

#Структура проекта

project/
├── app/
│   ├── __init__.py
│   ├── main.py          # Создание приложения
│   ├── config.py        # Настройки
│   ├── dependencies.py  # Зависимости
│   ├── exceptions.py    # Исключения
│   ├── api/
│   │   ├── __init__.py
│   │   ├── v1/
│   │   │   ├── __init__.py
│   │   │   ├── users.py
│   │   │   └── posts.py
│   │   └── v2/
│   ├── models/          # SQLAlchemy модели
│   ├── schemas/         # Pydantic схемы
│   ├── services/        # Бизнес-логика
│   ├── repositories/    # Доступ к БД
│   └── utils/           # Утилиты
├── tests/
├── alembic/
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── .env.example

#Код стайл

#Используйте type hints

# Плохо def get_user(id): return db.query(User).get(id) # Хорошо def get_user(user_id: int) -> User | None: return db.query(User).filter(User.id == user_id).first()

#Аннотируйте зависимости

# Плохо def get_db(): db = SessionLocal() try: yield db finally: db.close() # Хорошо from typing import Generator def get_db() -> Generator[Session, None, None]: db = SessionLocal() try: yield db finally: db.close()

#Используйте Annotated

from typing import Annotated # Хорошо (Python 3.10+) def get_user( user_id: Annotated[int, Path(..., ge=1)], db: Annotated[Session, Depends(get_db)] ): ...

#Безопасность

#Никогда не логируйте секреты

# Плохо logger.info(f"User password: {user.password}") # Хорошо logger.info(f"User {user.username} logged in")

#Валидируйте все входные данные

# Плохо @app.post('/users') def create_user(email: str): # Нет валидации email ... # Хорошо @app.post('/users') def create_user(user: UserCreate): # Pydantic валидирует email ...

#Используйте параметризованные запросы

# Плохо (SQL injection) query = f"SELECT * FROM users WHERE email = '{email}'" # Хорошо user = db.query(User).filter(User.email == email).first()

#Производительность

#Используйте async для I/O

# Плохо (блокирует) @app.get('/external') def get_external(): response = requests.get('https://api.example.com') return response.json() # Хорошо @app.get('/external') async def get_external(): async with httpx.AsyncClient() as client: response = await client.get('https://api.example.com') return response.json()

#Кэшируйте дорогие операции

from functools import lru_cache @lru_cache(maxsize=128) def get_config() -> dict: return load_config_from_file()

#Используйте пагинацию

# Плохо @app.get('/items') def get_items(): return db.query(Item).all() # Может быть огромным # Хорошо @app.get('/items') def get_items(skip: int = 0, limit: int = 100): return db.query(Item).offset(skip).limit(limit).all()

#Тестирование

#Пишите тесты для endpoints

def test_create_user(client): response = client.post('/users', json={ 'username': 'test', 'email': 'test@example.com', 'password': 'testpass123' }) assert response.status_code == 201 assert 'id' in response.json()

#Используйте фикстуры

@pytest.fixture def test_user(db): user = User(username='test', email='test@example.com') db.add(user) db.commit() return user def test_get_user(client, test_user): response = client.get(f'/users/{test_user.id}') assert response.status_code == 200

#Мокайте внешние сервисы

from unittest.mock import patch @patch('app.services.external_api.fetch_data') def test_with_mock(mock_fetch): mock_fetch.return_value = {'data': 'mocked'} response = client.get('/external') assert response.json() == {'data': 'mocked'}

#Документирование

#Документируйте endpoints

@app.get( '/users', summary="Получить список пользователей", description="Возвращает пагинированный список пользователей", response_description="Список пользователей" ) def get_users(...): """ Получает пользователей с поддержкой пагинации. ## Пример ответа ```json {"users": [...], "total": 100} ``` """ ...

#Используйте примеры

class UserCreate(BaseModel): username: str = Field(..., example="john_doe") email: EmailStr = Field(..., example="john@example.com")

#Мониторинг

#Добавьте health check

@app.get('/health') def health_check(): return {'status': 'healthy'}

#Логируйте запросы

@app.middleware("http") async def log_requests(request: Request, call_next): start_time = time.time() response = await call_next(request) duration = time.time() - start_time logger.info(f"{request.method} {request.url.path} - {duration:.3f}s") return response

#Инструментируйте метрики

from prometheus_fastapi_instrumentator import Instrumentator from contextlib import asynccontextmanager @asynccontextmanager async def lifespan(app): Instrumentator().instrument(app).expose(app) yield app = FastAPI(lifespan=lifespan)

#Чеклист перед production

#Безопасность

  • HTTPS включён
  • CORS настроен
  • Rate limiting включён
  • Пароли хешируются (Argon2)
  • SQL injection защищён
  • XSS защищён
  • CSRF токены для форм

#Производительность

  • Кэширование включено
  • Async для I/O
  • Пагинация для списков
  • Индексы в БД
  • N+1 запросы устранены

#Надёжность

  • Health checks
  • Graceful shutdown
  • Retry logic для внешних API
  • Circuit breaker
  • Логирование включено

#Мониторинг

  • Prometheus метрики
  • Grafana дашборды
  • Alerting настроен
  • Трейсинг (Jaeger)

#Деплой

  • Dockerfile оптимизирован
  • docker-compose для production
  • CI/CD pipeline
  • Переменные окружения
  • Бэкапы БД

#Антипаттерны

#1. Бизнес-логика в endpoints

# Плохо @app.post('/orders') def create_order(order: OrderCreate): # Валидация, расчёты, отправка email — всё здесь ... # Хорошо @app.post('/orders') def create_order(order: OrderCreate, service: OrderService = Depends()): return service.create_order(order)

#2. Прямой доступ к БД в endpoints

# Плохо @app.get('/users/{id}') def get_user(id: int, db: Session = Depends(get_db)): return db.query(User).get(id) # Хорошо @app.get('/users/{id}') def get_user(id: int, repo: UserRepository = Depends()): return repo.get_by_id(id)

#3. Игнорирование ошибок

# Плохо try: send_email() except: pass # Игнорирование ошибки # Хорошо try: send_email() except SMTPError as e: logger.error(f"Failed to send email: {e}") raise

#4. Синхронные операции в async

# Плохо @app.get('/slow') async def slow(): time.sleep(5) # Блокирует event loop return {'done': True} # Хорошо @app.get('/slow') async def slow(): await asyncio.sleep(5) return {'done': True}

#Исполняемая лаборатория

Лаборатория «Комплексный аудит: response_model, extra="forbid", статус-коды» доступна в публичном GitLab. API нарушает множество best practices: нет валидации, нет response_model, утекают внутренние поля, неправильные статус-коды, синхронные операции в async-эндпоинтах. Нужно разделить модели, добавить response_model, extra="forbid", правильные статус-коды и пагинацию.

Склонируйте репозиторий и запустите тесты из его корня:

git clone --branch v1.0 --depth 1 https://gitlab.potapov.me/courses/fastapi_pro.git cd fastapi_pro uv sync --group test uv run --group test pytest best_practices/tests

#Ресурсы

  • Официальная документация FastAPI
  • Pydantic документация
  • SQLAlchemy документация
  • OWASP Top 10