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 тиждень







