FastAPI: современный фреймворк для создания высокопроизводительных API
FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API с использованием Python 3.7+ на основе стандартов OpenAPI и JSON Schema. Он сочетает в себе скорость разработки, типизацию и автоматическую документацию, что делает его идеальным инструментом для создания надежных API.
В этой статье вы научитесь:
- Создавать API с помощью FastAPI и Pydantic
- Обрабатывать различные типы параметров (query, path, body)
- Работать с автоматической документацией Swagger UI
- Использовать типизированные модели данных для валидации
Почему FastAPI?
FastAPI быстро набирает популярность в Python-сообществе благодаря своим преимуществам:
- Высокая производительность — один из самых быстрых Python-фреймворков, сравнимый с Node.js и Go
- Автоматическая документация — генерирует интерактивную документацию Swagger UI и ReDoc
- Типизация Python 3.7+ — использует аннотации типов для валидации данных
- Простота и интуитивность — минималистичный синтаксис с максимальной функциональностью
- Асинхронная поддержка — встроенная поддержка async/await для высоконагруженных приложений
FastAPI идеально подходит для:
- Создания RESTful API и микросервисов
- Быстрой разработки прототипов и MVP
- Интеграции с машинным обучением и Data Science
- Создания внутренних инструментов и админ-панелей
Установка и первый запуск
Для начала работы с FastAPI необходимо установить сам фреймворк и ASGI-сервер для запуска приложения.
Установка зависимостей
pip install fastapi uvicorn
Результат выполнения в терминале:
Collecting fastapi
Downloading fastapi-0.103.2-py3-none-any.whl (86 kB)
Collecting uvicorn
Downloading uvicorn-0.23.2-py3-none-any.whl (58 kB)
Collecting pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,<3.0.0,>=1.7.4 (from fastapi)
Downloading pydantic-2.4.2-cp311-cp311-win_amd64.whl (2.2 MB)
Collecting starlette<0.28.0,>=0.27.0 (from fastapi)
Downloading starlette-0.27.0-py3-none-any.whl (64 kB)
Installing collected packages: pydantic, starlette, fastapi, uvicorn
Successfully installed fastapi-0.103.2 pydantic-2.4.2 starlette-0.27.0 uvicorn-0.23.2
Простейшее FastAPI-приложение
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Привет, мир!"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Результат выполнения в терминале:
INFO: Started server process [12345]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Теперь приложение доступно по адресу http://localhost:8000, а документация — по http://localhost:8000/docs.
Работа с параметрами в FastAPI
FastAPI предоставляет несколько способов получения данных из запросов: через параметры пути (path), через строку запроса (query) и из тела запроса (body).
Параметры пути (Path Parameters)
Параметры пути указываются непосредственно в пути URL и используются для идентификации конкретных ресурсов.
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}")
async def read_user(user_id: int):
return {"user_id": user_id, "name": f"User {user_id}"}
@app.get("/products/{product_id}/reviews")
async def get_product_reviews(product_id: str):
return {"product_id": product_id, "reviews": ["Отличный товар", "Неудобная упаковка"]}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Пример запроса и ответа:
GET /users/42
Response: {"user_id": 42, "name": "User 42"}
GET /products/book-123/reviews
Response: {"product_id": "book-123", "reviews": ["Отличный товар", "Неудобная упаковка"]}
Параметры строки запроса (Query Parameters)
Параметры строки запроса передаются после знака вопроса в URL и используются для фильтрации, пагинации и других операций.
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
@app.get("/search")
async def search_items(
query: str,
page: int = 1,
size: int = 10,
sort_by: Optional[str] = None
):
return {
"query": query,
"page": page,
"size": size,
"sort_by": sort_by,
"results": [f"Результат {i}" for i in range(1, size + 1)]
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Пример запроса и ответа:
GET /search?query=python&page=2&size=5&sort_by=date
Response: {
"query": "python",
"page": 2,
"size": 5,
"sort_by": "date",
"results": ["Результат 1", "Результат 2", "Результат 3", "Результат 4", "Результат 5"]
}
Параметры тела запроса (Request Body)
Для передачи сложных данных используется тело запроса в формате JSON. FastAPI автоматически парсит и валидирует эти данные.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
description: Optional[str] = None
in_stock: bool = True
@app.post("/items/")
async def create_item(item: Item):
return {
"message": "Товар успешно создан",
"item": item.dict(),
"total_cost": item.price * 1.2 # Пример бизнес-логики
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Пример запроса и ответа:
POST /items/
Body: {
"name": "Ноутбук",
"price": 999.99,
"description": "Мощный ноутбук для разработки",
"in_stock": true
}
Response: {
"message": "Товар успешно создан",
"item": {
"name": "Ноутбук",
"price": 999.99,
"description": "Мощный ноутбук для разработки",
"in_stock": true
},
"total_cost": 1199.988
}
Pydantic модели: User, Author, Post
Pydantic — это библиотека для валидации данных с использованием аннотаций типов Python. В FastAPI она используется для определения моделей данных, автоматической валидации и генерации документации.
Определение моделей
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, Field
from typing import List, Optional
from datetime import datetime
app = FastAPI()
class User(BaseModel):
id: int
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
is_active: bool = True
class Author(BaseModel):
id: int
name: str
bio: Optional[str] = None
user_id: int
class Post(BaseModel):
id: Optional[int] = None
title: str = Field(..., min_length=5, max_length=200)
content: str = Field(..., min_length=10)
author: Author
created_at: datetime = Field(default_factory=datetime.now)
tags: List[str] = []
class Config:
schema_extra = {
"example": {
"title": "Мой первый пост",
"content": "Это содержимое моего первого поста в блоге",
"author": {
"id": 1,
"name": "Иван Иванов",
"bio": "Профессиональный разработчик",
"user_id": 1
},
"tags": ["python", "fastapi", "web"]
}
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Особенности моделей:
Field(..., min_length=3)— задаёт ограничения на поляEmailStr— специальный тип для валидации emaildefault_factory=datetime.now— динамическое значение по умолчаниюschema_extra— примеры для документации SwaggerConfig— настройки модели
Создание API с моделями User, Author, Post
Теперь создадим полноценное API для управления пользователями, авторами и постами с использованием определённых ранее моделей.
Базовое CRUD API
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr, Field
from typing import List, Optional, Dict
from datetime import datetime
import uvicorn
app = FastAPI(title="Blog API", version="1.0.0")
# Модели данных
class User(BaseModel):
id: int
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
is_active: bool = True
class Author(BaseModel):
id: int
name: str
bio: Optional[str] = None
user_id: int
class Post(BaseModel):
id: Optional[int] = None
title: str = Field(..., min_length=5, max_length=200)
content: str = Field(..., min_length=10)
author_id: int
created_at: datetime = Field(default_factory=datetime.now)
tags: List[str] = []
# Имитация базы данных
users_db: Dict[int, User] = {}
authors_db: Dict[int, Author] = {}
posts_db: Dict[int, Post] = {}
user_counter = 1
author_counter = 1
post_counter = 1
# Эндпоинты для пользователей
@app.post("/users/", response_model=User)
async def create_user(user: User):
global user_counter
user.id = user_counter
users_db[user_counter] = user
user_counter += 1
return user
@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="Пользователь не найден")
return users_db[user_id]
# Эндпоинты для авторов
@app.post("/authors/", response_model=Author)
async def create_author(author: Author):
global author_counter
author.id = author_counter
authors_db[author_counter] = author
author_counter += 1
return author
@app.get("/authors/{author_id}", response_model=Author)
async def get_author(author_id: int):
if author_id not in authors_db:
raise HTTPException(status_code=404, detail="Автор не найден")
return authors_db[author_id]
# Эндпоинты для постов
@app.post("/posts/", response_model=Post)
async def create_post(post: Post):
global post_counter
post.id = post_counter
posts_db[post_counter] = post
post_counter += 1
return post
@app.get("/posts/{post_id}", response_model=Post)
async def get_post(post_id: int):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Пост не найден")
return posts_db[post_id]
@app.get("/posts/", response_model=List[Post])
async def list_posts(skip: int = 0, limit: int = 10):
return list(posts_db.values())[skip:skip + limit]
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Результат выполнения в терминале при запуске:
INFO: Started server process [12345]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Swagger UI: автоматическая документация API
Одной из самых мощных особенностей FastAPI является автоматическая генерация интерактивной документации с помощью Swagger UI.
Доступ к документации
После запуска приложения документация доступна по адресу:
http://localhost:8000/docs— интерактивная документация Swagger UIhttp://localhost:8000/redoc— альтернативная документация ReDoc
Пример использования Swagger UI
В Swagger UI вы можете:
- Просматривать все эндпоинты API
- Видеть параметры запросов и примеры тела запроса
- Выполнять тестовые запросы прямо из браузера
- Просматривать схемы данных (Pydantic модели)
- Экспортировать спецификацию OpenAPI в формате JSON/YAML
Пример интерфейса Swagger UI для нашего API:
{
"openapi": "3.1.0",
"info": {
"title": "Blog API",
"version": "1.0.0"
},
"paths": {
"/users/": {
"post": {
"summary": "Create User",
"operationId": "create_user_users__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/User"}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/User"}
}
}
}
}
}
},
"/posts/": {
"get": {
"summary": "List Posts",
"operationId": "list_posts_posts__get",
"parameters": [
{
"name": "skip",
"in": "query",
"schema": {"type": "integer", "default": 0}
},
{
"name": "limit",
"in": "query",
"schema": {"type": "integer", "default": 10}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {"$ref": "#/components/schemas/Post"}
}
}
}
}
}
},
"post": {
"summary": "Create Post",
"operationId": "create_post_posts__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Post"}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Post"}
}
}
}
}
}
}
},
"components": {
"schemas": {
"User": {
"properties": {
"id": {"type": "integer"},
"username": {"type": "string", "minLength": 3, "maxLength": 50},
"email": {"type": "string", "format": "email"},
"is_active": {"type": "boolean", "default": true}
},
"required": ["id", "username", "email"],
"type": "object"
},
"Post": {
"properties": {
"id": {"type": "integer"},
"title": {"type": "string", "minLength": 5, "maxLength": 200},
"content": {"type": "string", "minLength": 10},
"author_id": {"type": "integer"},
"created_at": {"type": "string", "format": "date-time"},
"tags": {
"items": {"type": "string"},
"type": "array",
"default": []
}
},
"required": ["title", "content", "author_id"],
"type": "object"
}
}
}
}
Тестирование API через Swagger UI
В Swagger UI можно протестировать любой эндпоинт:
- Выберите эндпоинт (например,
POST /users/) - Нажмите кнопку "Try it out"
- Введите пример данных в поле "Request body":
{
"username": "ivan_ivanov",
"email": "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ",
"is_active": true
}
- Нажмите "Execute"
- Посмотрите результат в разделе "Response body"
Практический пример: полный блог с авторизацией
Давайте создадим более сложный пример с аутентификацией и вложенными моделями.
from fastapi import FastAPI, Depends, HTTPException, status
from pydantic import BaseModel, EmailStr, Field
from typing import List, Optional
from datetime import datetime, timedelta
from jose import jwt
from passlib.context import CryptContext
app = FastAPI(title="Advanced Blog API", version="2.0.0")
# Настройки безопасности
SECRET_KEY = "your-secret-key-here"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Модели данных
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
password: str = Field(..., min_length=8)
class UserInDB(BaseModel):
id: int
username: str
email: EmailStr
hashed_password: str
is_active: bool = True
class Token(BaseModel):
access_token: str
token_type: str
class AuthorCreate(BaseModel):
name: str
bio: Optional[str] = None
class Author(BaseModel):
id: int
name: str
bio: Optional[str] = None
user_id: int
class PostCreate(BaseModel):
title: str = Field(..., min_length=5, max_length=200)
content: str = Field(..., min_length=10)
tags: List[str] = []
class Post(BaseModel):
id: int
title: str
content: str
author: Author
created_at: datetime
tags: List[str]
# Функции для работы с паролями
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 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})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
# Имитация базы данных
users_db = {}
authors_db = {}
posts_db = {}
user_counter = 1
author_counter = 1
post_counter = 1
# Эндпоинты аутентификации
@app.post("/token", response_model=Token)
async def login_for_access_token(user: UserCreate):
# В реальном приложении здесь должна быть проверка в БД
user_in_db = users_db.get(user.username)
if not user_in_db or not verify_password(user.password, user_in_db.hashed_password):
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"}
# Эндпоинты для пользователей
@app.post("/users/", response_model=UserInDB)
async def create_user(user: UserCreate):
global user_counter
if user.username in users_db:
raise HTTPException(status_code=400, detail="Пользователь уже существует")
hashed_password = get_password_hash(user.password)
new_user = UserInDB(
id=user_counter,
username=user.username,
email=user.email,
hashed_password=hashed_password
)
users_db[user.username] = new_user
user_counter += 1
return new_user
# Вспомогательная функция для получения текущего пользователя
def get_current_user():
# В реальном приложении здесь должна быть проверка токена
# Для упрощения возвращаем первого пользователя
return list(users_db.values())[0] if users_db else None
# Эндпоинты для авторов
@app.post("/authors/", response_model=Author)
async def create_author(author: AuthorCreate):
# В реальном приложении здесь должна быть проверка прав пользователя
global author_counter
current_user = get_current_user()
if not current_user:
raise HTTPException(status_code=401, detail="Необходима аутентификация")
new_author = Author(
id=author_counter,
name=author.name,
bio=author.bio,
user_id=current_user.id
)
authors_db[author_counter] = new_author
author_counter += 1
return new_author
# Эндпоинты для постов
@app.post("/posts/", response_model=Post)
async def create_post(post: PostCreate):
global post_counter
current_user = get_current_user()
if not current_user:
raise HTTPException(status_code=401, detail="Необходима аутентификация")
# Находим автора текущего пользователя
author = None
for auth in authors_db.values():
if auth.user_id == current_user.id:
author = auth
break
if not author:
raise HTTPException(status_code=400, detail="Автор не найден для пользователя")
new_post = Post(
id=post_counter,
title=post.title,
content=post.content,
author=author,
created_at=datetime.now(),
tags=post.tags
)
posts_db[post_counter] = new_post
post_counter += 1
return new_post
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Чек-лист для проверки усвоения материала
| № | Проверяемый навык / знание | Статус |
|---|---|---|
| 1 | Я понимаю, что такое FastAPI и какие преимущества он предоставляет. | |
| Я могу установить FastAPI и запустить базовое приложение. | ||
| Я знаю, как получить доступ к автоматической документации Swagger UI. | ||
| 2 | Я умею работать с параметрами пути (path parameters). | |
| Я умею работать с параметрами строки запроса (query parameters). | ||
| Я умею работать с параметрами тела запроса (request body) с помощью Pydantic. | ||
| 3 | Я понимаю назначение Pydantic и аннотаций типов в FastAPI. | |
| Я могу создавать и использовать Pydantic модели для валидации данных. | ||
| Я умею задавать ограничения на поля моделей (min_length, max_length, etc). | ||
| Я понимаю, как использовать вложенные модели и списки в Pydantic. | ||
| 4 | Я могу просматривать и тестировать API через Swagger UI. | |
| Я понимаю структуру OpenAPI спецификации, генерируемой FastAPI. | ||
| Я умею настраивать метаданные API (title, version, description). | ||
| 5 | Я могу создать CRUD API для работы с моделями данных. | |
| Я понимаю, как реализовать базовую аутентификацию в FastAPI. | ||
| Я умею обрабатывать ошибки и возвращать соответствующие HTTP статусы. | ||
| 6 | Я знаю о существовании асинхронных эндпоинтов (async/await) в FastAPI. | |
| Я понимаю, как интегрировать FastAPI с базами данных и внешними сервисами. |
Практическое задание для закрепления
- Создайте базовое FastAPI приложение с эндпоинтом
/health, который возвращает статус "ok". - Реализуйте CRUD API для управления товарами (Product) с полями:
id(int)name(str, min_length=2)price(float, > 0)description(optional str)in_stock(bool, default=True)
- Добавьте эндпоинты:
GET /products/— список товаров с пагинациейGET /products/{product_id}— получение товара по IDPOST /products/— создание нового товараPUT /products/{product_id}— обновление товараDELETE /products/{product_id}— удаление товара
- Настройте документацию:
- Добавьте заголовок и описание для API
- Настройте примеры для эндпоинтов в Swagger UI
- Реализуйте поиск:
- Добавьте эндпоинт
GET /products/searchс параметрамиqueryиmax_price - Реализуйте логику поиска по названию и фильтрации по цене
- Добавьте эндпоинт
После выполнения заданий вы будете уверенно создавать API с помощью FastAPI, понимать работу с различными типами параметров и эффективно использовать автоматическую документацию. Эти навыки являются основой для разработки современных веб-приложений и микросервисов.
Совет: Начинайте с простого — создайте базовое API и постепенно добавляйте сложные функции. Используйте Swagger UI для тестирования и отладки, это значительно ускорит процесс разработки.
Удачной разработки!
