Agent skill

api-contracts

Sync API contracts between FastAPI backend and React frontend. Triggers on "sync api", "update contracts", "generate types", "оновити контракти", "згенерувати типи", or when backend schemas change.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/design/api-contracts-djimontyp-task-tracker

SKILL.md

API Contracts

Автоматична синхронізація типів між FastAPI backend та React frontend через OpenAPI schema.

Overview

Backend (Pydantic) → openapi.json → Orval → TypeScript types + hooks

Commands

bash
# Повна синхронізація (рекомендовано)
just api-sync

# Окремі кроки
just api-export    # Експорт OpenAPI з FastAPI
just api-generate  # Генерація TypeScript з OpenAPI

When to Use

Викликай just api-sync коли:

  • Змінені Pydantic schemas в backend
  • Додані/видалені API endpoints
  • Змінені response/request models
  • Перед commit після backend змін

Workflow

1. Backend зміни

Після модифікації backend/app/schemas/*.py:

bash
just api-sync

2. Перевірка

Перевір згенеровані файли:

bash
# TypeScript types
ls frontend/src/shared/api/model/

# React Query hooks
ls frontend/src/shared/api/generated/

3. Використання в коді

typescript
// Імпорт згенерованих типів
import type { TopicPublic, AtomCreate } from '@/shared/api/model'

// Імпорт згенерованих hooks
import { useListTopicsApiV1TopicsGet } from '@/shared/api/generated/topics/topics'

// Використання hook
const { data, isLoading } = useListTopicsApiV1TopicsGet({ limit: 10 })

File Structure

contracts/
└── openapi.json              # OpenAPI schema (source of truth)

frontend/src/shared/api/
├── model/                    # TypeScript interfaces
│   ├── topicPublic.ts
│   ├── atomCreate.ts
│   └── ...
├── generated/                # React Query hooks
│   ├── topics/topics.ts
│   ├── atoms/atoms.ts
│   └── ...
└── lib/api/
    └── mutator.ts            # Axios wrapper for orval

Configuration

Orval config: frontend/orval.config.ts

typescript
export default defineConfig({
  api: {
    input: '../contracts/openapi.json',
    output: {
      mode: 'tags-split',
      target: './src/shared/api/generated',
      schemas: './src/shared/api/model',
      client: 'react-query',
    },
  },
})

Troubleshooting

Import errors after generation

bash
# Перегенерувати з чистого стану
cd frontend && rm -rf src/shared/api/generated src/shared/api/model
just api-sync

Backend changes not reflected

bash
# Перевірити що схема оновлена
cat contracts/openapi.json | jq '.paths | keys | length'

# Порівняти з runtime
curl http://localhost/api/v1/openapi.json | jq '.paths | keys | length'

TypeScript errors

bash
cd frontend && npx tsc --noEmit

Notes

  • Згенеровані файли комітяться в git
  • Hooks використовують customInstance з lib/api/mutator.ts
  • Query keys автоматично генеруються з endpoint paths

Token-Efficient OpenAPI Access

Файл: contracts/openapi.json (~12k рядків, 99 endpoints, 132 schemas)

Для економії контексту рекомендовано витягувати тільки потрібну інформацію через Python.

OpenAPI 3.0 Структура

openapi.json
├── openapi: "3.0.2"              # версія специфікації
├── info                          # метадані API
│   ├── title
│   ├── version
│   └── description
├── paths                         # 🎯 ENDPOINTS (dict: path → methods)
│   └── /api/v1/topics
│       ├── get
│       │   ├── summary
│       │   ├── tags: []
│       │   ├── parameters: []    # query/path params
│       │   └── responses
│       │       └── 200
│       │           └── content.application/json.schema.$ref
│       └── post
│           ├── requestBody       # body schema
│           │   └── content.application/json.schema.$ref
│           └── responses
├── components                    # 🎯 REUSABLE DEFINITIONS
│   └── schemas                   # TypeScript types живуть тут
│       └── TopicPublic
│           ├── type: "object"
│           ├── properties        # поля
│           │   ├── id: {type: "string"}
│           │   └── name: {type: "string"}
│           └── required: []
└── tags                          # групування endpoints
    └── [{name: "topics", description: "..."}]

Навігація по дереву

python
spec = json.load(open('contracts/openapi.json'))

# Рівень 1: Корінь
spec.keys()  # ['openapi', 'info', 'paths', 'components', 'tags']

# Рівень 2: Endpoints
spec['paths'].keys()  # всі шляхи API

# Рівень 3: Methods
spec['paths']['/api/v1/topics'].keys()  # ['get', 'post', ...]

# Рівень 4: Деталі endpoint
spec['paths']['/api/v1/topics']['get'].keys()  # ['summary', 'tags', 'parameters', 'responses']

# Рівень 2: Schemas
spec['components']['schemas'].keys()  # всі типи

# Рівень 3: Schema definition
spec['components']['schemas']['TopicPublic'].keys()  # ['type', 'properties', 'required']

Приклади (адаптуй під задачу)

python
import json
spec = json.load(open('contracts/openapi.json'))

# Список всіх endpoints
for p, ms in spec['paths'].items():
    for m in ms:
        if m in ('get','post','put','patch','delete'):
            print(f'{m.upper():6} {p}')

# Список schemas
for name in spec['components']['schemas']: print(name)

# Пошук endpoints по keyword
kw = 'topic'
[print(f'{m.upper():6} {p}') for p,ms in spec['paths'].items() for m in ms if kw in p and m in ('get','post','put','delete')]

# Деталі endpoint
print(json.dumps(spec['paths']['/api/v1/topics']['get'], indent=2))

# Деталі schema
print(json.dumps(spec['components']['schemas']['TopicPublic'], indent=2))

# Тільки поля schema
print(list(spec['components']['schemas']['TopicPublic'].get('properties', {}).keys()))

# Endpoints з певним tag
tag = 'topics'
[print(f"{m.upper():6} {p}") for p,ms in spec['paths'].items() for m,d in ms.items() if m in ('get','post','put','delete') and tag in d.get('tags',[])]

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results