AI-генерация API из текстовых описаний (Text-to-API)
Text-to-API — это генерация полноценного REST/GraphQL API-слоя из описания на естественном языке или OpenAPI-спецификации. Задача шире, чем просто написать endpoints: нужны модели данных, валидация, middleware, тесты, документация. AI выступает как junior-разработчик с хорошим знанием FastAPI/Express, который работает со скоростью тысячи строк в минуту.
Архитектура генератора API
from anthropic import Anthropic
from pathlib import Path
import json
from pydantic import BaseModel
from typing import Literal, Optional
client = Anthropic()
class APIEndpoint(BaseModel):
method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
path: str
summary: str
request_body: Optional[dict] = None
response_schema: dict
auth_required: bool = True
query_params: list[dict] = []
class APISpec(BaseModel):
title: str
description: str
version: str
base_path: str
endpoints: list[APIEndpoint]
entities: list[dict] # Бизнес-сущности
class TextToAPIGenerator:
def parse_description(self, description: str) -> APISpec:
"""Парсит текстовое описание в структурированную спецификацию"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
system="""Ты — API-архитектор. Парсишь описание системы в структуру REST API.
Правила REST:
- Существительные в URL (не глаголы): /users, /orders
- Правильные HTTP методы: GET=чтение, POST=создание, PUT=полная замена, PATCH=частичное обновление, DELETE=удаление
- Вложенность максимум 2 уровня: /users/{id}/orders
- Plural для коллекций: /products, /categories
- Пагинация: ?page=1&limit=20
- Фильтрация: ?status=active&created_after=2024-01-01""",
messages=[{
"role": "user",
"content": f"""Разбери описание и верни JSON спецификацию API:
{{
"title": "...",
"description": "...",
"version": "1.0.0",
"base_path": "/api/v1",
"entities": [
{{"name": "...", "fields": [{{"name": "...", "type": "...", "required": true}}]}}
],
"endpoints": [
{{
"method": "GET|POST|PUT|PATCH|DELETE",
"path": "/resource/{{id}}",
"summary": "...",
"auth_required": true,
"request_body": {{"field": "type"}},
"response_schema": {{"id": "int", "name": "str"}},
"query_params": [{{"name": "...", "type": "...", "required": false}}]
}}
]
}}
Описание системы:
{description}"""
}]
)
text = response.content[0].text
start = text.find("{")
end = text.rfind("}") + 1
data = json.loads(text[start:end])
return APISpec(**data)
def generate_fastapi_code(self, spec: APISpec) -> dict[str, str]:
"""Генерирует полный FastAPI проект"""
files = {}
# models.py
files["models.py"] = self._generate_models(spec)
# schemas.py
files["schemas.py"] = self._generate_schemas(spec)
# routers/{resource}.py для каждой сущности
for entity in spec.entities:
router_code = self._generate_router(entity, spec)
files[f"routers/{entity['name'].lower()}.py"] = router_code
# main.py
files["main.py"] = self._generate_main(spec)
# tests/
for entity in spec.entities:
test_code = self._generate_tests(entity, spec)
files[f"tests/test_{entity['name'].lower()}.py"] = test_code
return files
def _generate_models(self, spec: APISpec) -> str:
"""Генерирует SQLAlchemy модели"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Создай SQLAlchemy 2.0 модели для сущностей:
Сущности:
{json.dumps(spec.entities, ensure_ascii=False, indent=2)}
Требования:
- Используй DeclarativeBase
- Добавь id (Integer PK autoincrement), created_at, updated_at для всех моделей
- Используй правильные типы: String(256), Text, Integer, Float, Boolean, DateTime
- Добавь __tablename__
- Добавь relationship() для связей между моделями
- Добавь __repr__ для дебага
Верни только Python код."""
}]
)
return response.content[0].text.strip()
def _generate_schemas(self, spec: APISpec) -> str:
"""Генерирует Pydantic v2 схемы"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Создай Pydantic v2 схемы для валидации:
Сущности: {json.dumps(spec.entities, ensure_ascii=False)}
Endpoints: {json.dumps([e.dict() for e in spec.endpoints], ensure_ascii=False)}
Для каждой сущности создай:
- <Entity>Create — для POST (все обязательные поля)
- <Entity>Update — для PATCH (все поля Optional)
- <Entity>Response — для ответов (включая id, created_at)
- <Entity>ListResponse — с пагинацией
Добавь field validators где нужно (email формат, позитивные числа, длина строк).
Верни только Python код."""
}]
)
return response.content[0].text.strip()
def _generate_router(self, entity: dict, spec: APISpec) -> str:
"""Генерирует роутер с CRUD endpoints"""
entity_endpoints = [
e for e in spec.endpoints
if entity["name"].lower() in e.path.lower()
]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Создай FastAPI роутер для сущности {entity['name']}.
Endpoints для реализации:
{json.dumps([e.dict() for e in entity_endpoints], ensure_ascii=False, indent=2)}
Требования:
- Используй APIRouter с prefix и tags
- Dependency injection для DB session (AsyncSession)
- Dependency injection для авторизации (get_current_user)
- Async/await для всех операций
- Правильные HTTP статусы: 201 для POST, 204 для DELETE, 404 если не найдено
- Пагинация через query params page/limit
- Логирование через structlog
Верни только Python код."""
}]
)
return response.content[0].text.strip()
def _generate_main(self, spec: APISpec) -> str:
return f"""from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
{chr(10).join(f"from routers.{e['name'].lower()} import router as {e['name'].lower()}_router" for e in spec.entities)}
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
yield
# Shutdown
app = FastAPI(
title="{spec.title}",
description="{spec.description}",
version="{spec.version}",
lifespan=lifespan,
)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
{chr(10).join(f'app.include_router({e["name"].lower()}_router, prefix="{spec.base_path}")' for e in spec.entities)}
"""
def _generate_tests(self, entity: dict, spec: APISpec) -> str:
"""Генерирует pytest тесты для CRUD endpoints"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
messages=[{
"role": "user",
"content": f"""Создай pytest тесты для CRUD endpoints сущности {entity['name']}.
Используй:
- pytest-asyncio для async тестов
- httpx.AsyncClient для HTTP запросов
- pytest fixtures для setup/teardown
- TestDatabase (SQLite in-memory) для изоляции
Покрой: создание, чтение списка, чтение одного, обновление, удаление, 404 случаи, валидацию входных данных.
Верни только Python код."""
}]
)
return response.content[0].text.strip()
CLI интерфейс
import click
import yaml
@click.command()
@click.argument("description_file", type=click.Path(exists=True))
@click.option("--output-dir", "-o", default="./generated_api")
@click.option("--framework", default="fastapi", type=click.Choice(["fastapi", "express"]))
def generate_api(description_file: str, output_dir: str, framework: str):
"""Генерирует API из текстового описания"""
description = Path(description_file).read_text()
generator = TextToAPIGenerator()
click.echo("Parsing description...")
spec = generator.parse_description(description)
click.echo(f"Found {len(spec.endpoints)} endpoints, {len(spec.entities)} entities")
click.echo("Generating code...")
files = generator.generate_fastapi_code(spec)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
for filename, content in files.items():
file_path = output_path / filename
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(content)
click.echo(f" Created: {filename}")
# Генерируем docker-compose и requirements.txt
_generate_project_files(output_path, spec)
click.echo(f"\nAPI generated in {output_dir}")
click.echo("Run: cd generated_api && docker-compose up")
if __name__ == "__main__":
generate_api()
Пример описания для генерации
# Система управления задачами
Многопользовательская система для команд.
Сущности:
- User: email (уникальный), name, role (admin/member), avatar_url
- Team: name, description, owner_id
- Project: name, description, team_id, status (active/archived)
- Task: title, description, project_id, assignee_id, status (todo/in_progress/done), priority (low/medium/high), due_date
Функциональность:
- Регистрация и авторизация (JWT)
- CRUD для команд, проектов, задач
- Назначение задач участникам команды
- Фильтрация задач по статусу, исполнителю, приоритету
- Пагинация всех списков
- Soft delete для задач
Практический кейс: внутренний B2B-продукт
Задача: стартап хотел за 2 недели получить MVP backend для marketplace услуг. 8 основных сущностей, 45+ endpoints.
Генерация:
- Описание продукта на 2 страницы → APISpec (30 секунд)
- Генерация кода FastAPI (7 файлов + тесты) → 8 минут
- Ручная доработка бизнес-логики авторизации → 3 дня
- Интеграция платёжного шлюза → 2 дня
Результат: работающий MVP за 5 дней вместо запланированных 14. Test coverage 67% (тесты были сгенерированы автоматически).
Что AI генерирует хорошо: CRUD, пагинация, валидация, структура проекта, тесты happy path.
Что требует рук: сложная бизнес-логика авторизации, специфичные алгоритмы ценообразования, нетривиальные SQL запросы с оконными функциями.
Сроки
- Прототип генератора (описание → один файл): 2–3 дня
- Полная генерация проекта с тестами: 1–2 недели
- Поддержка дополнительных фреймворков (Express, Django REST): +1 неделя каждый
- Интеграция в CI/CD для регенерации при изменении спецификации: 1 неделя







