Tworzenie API z AI: najpierw kontrakt, weryfikacja maszynowa
ENTworzenie API z agentami AI działa, gdy kontrakt powstaje pierwszy: człowiek przegląda krótki kontrakt OpenAPI, GraphQL lub protobuf, agent implementuje po jednym wycinku endpointów zgodnie z nim, a bramki maszynowe (lint kontraktu, testy, fuzzing kontraktowy, diff zmian łamiących i progi obciążeniowe) dowodzą zgodności handlerów. Recenzent czyta kontrakt i wyniki bramek, a nie każdy handler.
Twój endpoint GET /tasks ładuje osobę przypisaną do każdego zadania osobnym zapytaniem. Przeszedł review i demo, bo demo miało trzy zadania. Na produkcji tablica renderuje 50 zadań, handler wysyła 51 zapytań, a pula połączeń wyczerpuje się przed południem. Agent napisał to, o co prosił prompt. Ani prompt, ani kontrakt, ani bramki nie mówiły, jak endpoint będzie wywoływany.
Ta strona jest dla programisty, który buduje endpointy, i dla tech leada, który je zatwierdza bez czytania 2000 linii handlerów. Opisuje metodę niezależnie od narzędzia i protokołu. Implementacje dla konkretnych narzędzi znajdziesz w artykułach budowanie API w Claude Code i API „najpierw kontrakt” w Codeksie.
Co daje praca nad API z agentem, gdy kontrakt jest pierwszy
Dział zatytułowany „Co daje praca nad API z agentem, gdy kontrakt jest pierwszy”- Pętlę sześciu kroków, w której o kształcie API decyduje kontrakt, a nie agent.
- Cztery prompty do skopiowania: szkic kontraktu, implementacja wycinka, test pokrycia autoryzacją i wycofanie wersji.
- Tabelę bramek i skrypt CI, które sprawdzają implementację względem kontraktu REST, wraz z odpowiednikami dla GraphQL i gRPC.
- Wariant każdego kroku agenta dla Claude Code, Codeksa i Cursora.
- Typowe awarie API budowanych przez agentów i sposób wyjścia z każdej z nich.
Kto co czyta, gdy API buduje agent?
Dział zatytułowany „Kto co czyta, gdy API buduje agent?”Podejście „najpierw kontrakt” przenosi uwagę człowieka na najmniejszy artefakt, który definiuje zachowanie. Plik OpenAPI na 150 linii dla pięciu endpointów da się rzetelnie przejrzeć. Kod za nim sprawdzają maszyny.
| Artefakt | Kto sprawdza | Jak |
|---|---|---|
Kontrakt (api/openapi.yaml, schema.graphql lub .proto) | Ty, linia po linii; właściciel API zatwierdza przez CODEOWNERS | Pull request zmieniający wyłącznie kontrakt |
| Styl i poprawność kontraktu | Maszyna | Lint Spectral lub Redocly, buf lint, walidacja schematu GraphQL |
| Zgodność z konsumentami | Maszyna | oasdiff breaking, buf breaking, graphql-inspector diff względem main |
| Handlery, walidacja, zapytania | Maszyna | Sprawdzanie typów, testy jednostkowe i integracyjne |
| Zgodność handlerów z kontraktem | Maszyna | Schemathesis na uruchomionym serwerze |
| Opóźnienie i liczba zapytań pod obciążeniem | Maszyna | k6 z progami, które kończą przebieg błędem |
| Ścieżki autoryzacji, pieniędzy i usuwania danych | Agent recenzujący, potem człowiek, tylko na oznaczonych plikach | /code-review, codex review, Bugbot |
Programista, do którego należy gałąź, zatwierdza diff kontraktu i wyniki bramek. Tech lead odpowiada za listę bramek: bramka dodana do listy obejmuje każdy przyszły endpoint, a komentarz w review tylko jeden.
Jak przebiega pętla „najpierw kontrakt”?
Dział zatytułowany „Jak przebiega pętla „najpierw kontrakt”?”-
Naszkicuj kontrakt z agentem. Poproś wyłącznie o specyfikację, z wpisaną paginacją, autoryzacją, formatem błędów i wzorcami dostępu. Bez kodu serwera.
-
Przepuść go przez linter i przejrzyj osobiście. Uruchom linter kontraktu, potem przeczytaj diff. To jedyny artefakt, który czytasz w całości.
-
Scal kontrakt osobno. Pull request z kontraktem zmienia tylko plik kontraktu. Gałęzie implementacyjne traktują go jako tylko do odczytu.
-
Implementuj jeden wycinek na zadanie. Jeden zasób lub kilka operacji na sesję agenta, we własnej gałęzi lub worktree, względem scalonego kontraktu.
-
Uruchom bramki. Testy, fuzzing kontraktowy, test pokrycia autoryzacją, a dla endpointów listujących także test obciążeniowy. Agent uruchamia je przed zgłoszeniem „gotowe”, a CI uruchamia je ponownie.
-
Sprawdź zgodność wsteczną i wygeneruj klientów. Diff zmian łamiących względem
mainblokuje niezamierzone złamanie kontraktu. SDK klientów generujesz z kontraktu, nigdy nie edytujesz ich ręcznie.
Jak spisać zachowanie, zanim powstanie jakikolwiek kontrakt, opisuje spec-driven development.
Naszkicuj kontrakt przed pierwszym handlerem
Dział zatytułowany „Naszkicuj kontrakt przed pierwszym handlerem”Nazwij konwencje w prompcie. Agent, któremu zostawisz wybór paginacji, formatu błędów i kodów statusu, przy każdym endpoincie wybierze inaczej.
Ostatnia linia ma znaczenie. Otwarte pytania agenta to decyzje, których nikt nie podjął, a ich miejsce jest w review kontraktu, nie w handlerze.
Zanim przeczytasz wynik, przepuść go przez linter. Spectral potrzebuje pliku z zestawem reguł:
# Terminal, repo rootecho 'extends: ["spectral:oas"]' > .spectral.yamlnpx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warnTa sama dyscyplina dotyczy innych protokołów. Zmieniają się tylko narzędzia:
| Protokół | Kontrakt | Lint | Kontrola zmian łamiących |
|---|---|---|---|
| REST | api/openapi.yaml (OpenAPI 3.1) | npx @stoplight/spectral-cli lint lub npx @redocly/cli lint | oasdiff breaking --fail-on ERR base.yaml api/openapi.yaml |
| GraphQL | schema.graphql z typami connection dla list | npx @graphql-inspector/cli validate 'src/**/*.graphql' schema.graphql (sprawdza każdą operację względem schematu) | npx @graphql-inspector/cli diff old.graphql schema.graphql |
| gRPC | Pliki .proto z field mask dla częściowych aktualizacji | npx @bufbuild/buf lint | npx @bufbuild/buf breaking --against '.git#branch=main' |
Wersje sprawdzone 26 września 2026: @stoplight/spectral-cli 6.16.3, @redocly/cli 2.57.0, @graphql-inspector/cli 7.0.0, @bufbuild/buf 1.73.0 w npm. oasdiff zainstalujesz przez go install github.com/oasdiff/oasdiff@latest lub brew install oasdiff.
Zapisz konwencje API tam, gdzie czyta je każda sesja
Dział zatytułowany „Zapisz konwencje API tam, gdzie czyta je każda sesja”Konwencje powtarzane w każdym prompcie się rozjeżdżają. Zapisz je raz, w pliku, który każde narzędzie wczytuje na początku sesji:
## API conventions- The contract (api/openapi.yaml) is the source of truth. Implementation tasks never edit it. If the contract looks wrong, stop and say so.- Middleware order: requestId → requireAuth → validate (Zod, from the contract) → handler → errorHandler.- Handlers are thin: parse, call the service, map errors. Queries live in src/db/queries/.- Lists never query per row: batch with a join or an IN query (DataLoader for GraphQL resolvers).- Gates before "done": bash ci/api-gates.sh (Spectral, typecheck, tests, oasdiff, Schemathesis).Umieść tę sekcję w CLAUDE.md w katalogu głównym repozytorium. Aby „gotowe” oznaczało zielone bramki, dodaj hook Stop, który je uruchamia i przy porażce blokuje zakończenie tury. Pełny skrypt hooka i jego rejestrację znajdziesz w artykule budowanie API w Claude Code, a zdarzenia hooków w automatyzacji hookami.
Umieść tę sekcję w AGENTS.md w katalogu głównym repozytorium; Codex czyta go przed każdym zadaniem. Sesja w sandboksie nie ma dostępu do sieci, więc uvx schemathesis i oasdiff nie pobiorą się w niej. Albo zainstaluj wcześniej wszystkie narzędzia bramek (npm install -D @stoplight/spectral-cli, uv tool install schemathesis oraz brew install oasdiff lub go install github.com/oasdiff/oasdiff@latest), albo niech agent uruchamia lokalnie część offline (Spectral, typecheck, testy), a pełny ci/api-gates.sh zostaw CI. Każdy wycinek uruchamiaj we własnym worktree przez codex --worktree. Pętlę wycinek po wycinku opisuje API „najpierw kontrakt” w Codeksie.
Zapisz tę sekcję jako regułę projektu w .cursor/rules/ (na przykład api-conventions.mdc) ustawioną na stosowanie zawsze. Format i test kanarkowy, który potwierdza wczytanie reguły, opisuje artykuł reguły projektu w Cursorze. Pierwszy wycinek zrób w Plan Mode, żeby zatwierdzić listę plików, zanim agent zacznie edytować.
Który model ma budować endpointy?
Dział zatytułowany „Który model ma budować endpointy?”Przy wyborze modelu zacznij od domyślnego modelu narzędzia: 12 października 2026 to Claude Opus 5.5 w Claude Code (od v2.1.280, na obu kanałach wydań) i GPT-6.1 Sol w Codeksie (domyślny od CLI 0.159.1). Najpierw podnieś poziom effort, a model zmieniaj dopiero wtedy, gdy wskażą to twoje własne wyniki. Aktualne opcje znajdziesz w przeglądzie modeli.
Zaimplementuj jeden wycinek zgodnie z kontraktem
Dział zatytułowany „Zaimplementuj jeden wycinek zgodnie z kontraktem”Wycinek to najmniejszy zestaw operacji, który ma sens sam w sobie, na przykład listowanie i tworzenie jednego zasobu. Mniejsze wycinki sprawiają, że każdy diff i każdy przebieg bramek da się przeczytać.
Testy, które agent pisze w tej samej sesji, zgadzają się z jego kodem, niekoniecznie z kontraktem. Dlatego istnieje opisana niżej bramka fuzzingu kontraktowego: generuje żądania z kontraktu, więc nie dzieli założeń agenta.
Daj agentowi prawdziwy schemat bazy
Dział zatytułowany „Daj agentowi prawdziwy schemat bazy”Przy endpointach opartych na bazie agent, który zgaduje nazwy kolumn, funduje ci rundę poprawek. Serwer MCP bazy danych pozwala mu zamiast tego odczytać prawdziwy schemat i plany zapytań. Postgres MCP Pro (postgres-mcp; 0.3.0 to ostatnie wydanie w PyPI, z 16 maja 2025; stan na 26 września 2026) uruchamiasz przez uvx; podłącz go do bazy stagingowej przez rolę tylko do odczytu:
# Terminal, repo root: ${STAGING_DATABASE_URL} is expanded from your shell at launchclaude mcp add -s project postgres -e 'DATABASE_URI=${STAGING_DATABASE_URL}' \ -- uvx postgres-mcp --access-mode=restricted# ~/.codex/config.toml; export DATABASE_URI before you start codex[mcp_servers.postgres]command = "uvx"args = ["postgres-mcp", "--access-mode=restricted"]env_vars = ["DATABASE_URI"]// ~/.cursor/mcp.json; the value comes from your shell or secret manager, never the file{ "mcpServers": { "postgres": { "command": "uvx", "args": ["postgres-mcp", "--access-mode=restricted"], "env": { "DATABASE_URI": "${env:STAGING_DATABASE_URL}" } } }}Interpolacja ${env:NAZWA} opiera się na dokumentacji MCP Cursora; sprawdź ją w swojej wersji Cursora. Jeśli się nie rozwiązuje, usuń blok env i uruchamiaj Cursora z powłoki, która eksportuje DATABASE_URI.
Po podłączeniu serwera dopisz do promptu wycinka „run explain_query on every new query and report sequential scans”. W projektach z Prismą lżejszą opcją jest pakiet prisma/skills (npx skills add prisma/skills): uczy agenta CLI i API klienta Prismy bez połączenia z bazą. Inne serwery i ich pułapki opisuje artykuł serwery MCP dla baz danych.
Jak udowodnić zgodność API z kontraktem bez czytania każdego handlera?
Dział zatytułowany „Jak udowodnić zgodność API z kontraktem bez czytania każdego handlera?”Każda bramka łapie klasę błędów, którą przegapia recenzent przeglądający duży diff. Pierwsze pięć działa w CI przy każdym pull requeście.
| Bramka | Co łapie | Polecenie |
|---|---|---|
| Lint kontraktu | Brakujące odpowiedzi błędów, niespójne nazwy, nieudokumentowane operacje | npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn |
| Typy i testy | Złe kody statusu, zepsutą walidację, paginację, w której przekazany z powrotem nextCursor nie zwraca kolejnej strony | npm run typecheck && npm test |
| Test pokrycia autoryzacją | Trasę zarejestrowaną bez requireAuth | tests/auth-coverage.test.ts (prompt niżej) |
| Fuzzing kontraktowy | Odpowiedzi niezgodne ze schematem, błędy 500 na nietypowych danych, endpointy działające bez poświadczeń | uvx schemathesis run api/openapi.yaml --url "$BASE_URL" --checks all |
| Diff zmian łamiących | Usunięte pola, nowe wymagane parametry | oasdiff breaking --fail-on ERR base.yaml api/openapi.yaml |
| Progi obciążeniowe | Zapytania N+1, wolne endpointy listujące | k6 run load/tasks.js (na endpointach listujących i przed wydaniem) |
Schemathesis (4.28.0 w PyPI, 26 września 2026) czyta kontrakt, generuje poprawne i niepoprawne żądania oraz sprawdza kody statusu, schematy odpowiedzi i nagłówki. Jego sprawdzenie ignored_auth oznacza operację, która nadal się udaje, gdy pominiesz poświadczenia wymagane przez kontrakt. Uruchamiaj go wyłącznie na środowisku lokalnym lub jednorazowym: wysyła dużo wygenerowanych żądań, także zapisów.
Test pokrycia autoryzacją warto napisać pierwszego dnia, bo trasa bez autoryzacji przechodzi wszystkie inne bramki, jeśli testy pisał ten sam agent, który zapomniał o middleware:
Ostatnie polecenie jest tu najważniejsze: bramka, której nigdy nie widziałeś czerwonej, to bramka, której nie przetestowałeś. Dlaczego agent nie powinien mieć możliwości edytowania tych bramek, wyjaśnia artykuł chroń wyrocznię testową.
Zwięzłe zadanie CI dla bramek REST, z kontraktem z main jako punktem odniesienia dla zgodności:
#!/usr/bin/env bash# ci/api-gates.sh: run in CI with full history (fetch-depth: 0) and the server started# against a disposable database. TEST_API_TOKEN comes from the CI secret store.set -euo pipefailBASE_URL="${BASE_URL:-http://localhost:3000}"
npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warnnpm run typechecknpm test
# Fail closed when main was not fetched; skip only when main has no contract yet.git rev-parse --verify -q origin/main > /dev/null || { echo "origin/main not fetched"; exit 1; }if git show origin/main:api/openapi.yaml > /tmp/openapi-base.yaml; then oasdiff breaking --fail-on ERR /tmp/openapi-base.yaml api/openapi.yamlelse echo "::warning::no base contract on main, breaking-change check skipped"fi
curl -sf "$BASE_URL/health" > /dev/nulluvx schemathesis run api/openapi.yaml --url "$BASE_URL" --checks all \ --header "Authorization: Bearer ${TEST_API_TOKEN:?}" \ --report junit --report-dir reports/schemathesisZadaniem agenta jest doprowadzić te bramki do zieleni, zanim poprosi o review. Każde narzędzie robi to inaczej:
Hook Stop uruchamia bramki, gdy Claude próbuje zakończyć turę, i oddaje mu błąd, więc sesja trwa, dopóki bramki nie przejdą; niech hook sprawdza stop_hook_active, żeby nie zapętlił się w nieskończoność. Potem uruchom w sesji /code-review albo subagenta recenzującego z uprawnieniami tylko do odczytu, który porównuje zmienione trasy z kontraktem. Oba opisuje artykuł budowanie API w Claude Code.
Wypisz polecenia bramek w AGENTS.md w sekcji „Gates before done”, a gałąź przejrzyj, wpisując gałąź bazową i sprawdzenie kontraktu w instrukcje: codex review "Review this branch's changes against main: compare every changed route with api/openapi.yaml and flag any missing requireAuth". --base i własny prompt wykluczają się wzajemnie (sprawdzone w Codex CLI 0.157.1), więc jeśli wolisz codex review --base main, przenieś sprawdzenie kontraktu do AGENTS.md. Gdy Schemathesis zgłosi błędy, przekaż Codeksowi raport JUnit zamiast poprawiać je samodzielnie; prompt do tego znajdziesz w artykule API „najpierw kontrakt” w Codeksie.
Pozwól agentowi uruchomić polecenia bramek w zintegrowanym terminalu i przeczytać wynik w tej samej sesji. Na pull requeście Bugbot szuka błędów i problemów z bezpieczeństwem. Hook projektu w .cursor/hooks.json może blokować polecenia, których nigdy nie chcesz uruchamiać, na przykład fuzzing produkcyjnego adresu.
O tym, czy kontrakt opisuje właściwy produkt, nadal decyduje człowiek. Bramki dowodzą tylko, że kod robi to, co mówi kontrakt. Rutynę review samego pull requesta opisuje artykuł review pull requestów od agentów.
Wyłap zapytania N+1 i wolne listy przed produkcją
Dział zatytułowany „Wyłap zapytania N+1 i wolne listy przed produkcją”Demo z trzema wierszami nigdy nie pokaże zapytania N+1. Test obciążeniowy z progami je pokaże, bo wolny lub nieudany przebieg kończy się kodem niezerowym, zamiast tylko źle wyglądać na wykresie. k6 to samodzielny plik binarny, a nie pakiet npm czy PyPI: zainstaluj go przez brew install k6 lub winget install k6 albo uruchom w Dockerze poleceniem docker run --rm -i grafana/k6 run - <load/tasks.js.
// load/tasks.js: run with k6 run -e BASE_URL=http://localhost:3000 -e TOKEN=... load/tasks.jsimport http from 'k6/http';import { check } from 'k6';
export const options = { stages: [ { duration: '1m', target: 50 }, { duration: '3m', target: 50 }, { duration: '1m', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<300'], http_req_failed: ['rate<0.01'], },};
export default function () { const res = http.get(`${__ENV.BASE_URL}/tasks?limit=100`, { headers: { Authorization: `Bearer ${__ENV.TOKEN}` }, }); check(res, { 'status is 200': (r) => r.status === 200 });}Najpierw wypełnij bazę realistyczną ilością danych: co najmniej kilka tysięcy wierszy i po kilka powiązanych rekordów na wiersz. Progi ustaw na podstawie swoich celów SLO, nie liczb z przykładu. Jeśli chcesz sprawdzić liczbę zapytań bez obciążenia, sprawdź asercją liczbę zapytań na żądanie w teście integracyjnym. Oba podejścia opisuje artykuł testy wydajnościowe.
Wersjonuj i wycofuj API, nie psując konsumentów
Dział zatytułowany „Wersjonuj i wycofuj API, nie psując konsumentów”Diff zmian łamiących mówi, kiedy zmiana wymaga nowej wersji. Najpierw scal api/v2/openapi.yaml w osobnym pull requeście, który zmienia tylko kontrakt, jak każdą zmianę kontraktu; pull request z implementacją zmienia wtedy wyłącznie src/ i przechodzi strażnika kontraktu opisanego niżej w trybach awarii. Starą wersję wycofuj standardowymi nagłówkami, żeby klienci i bramy API mogli to wykryć: Deprecation (RFC 9745) przyjmuje datę w formacie structured field, czyli @ i znacznik czasu Unix, a Sunset (RFC 8594) datę HTTP.
Po każdej zmianie kontraktu wygeneruj klientów z kontraktu, żeby konsumenci szli razem z serwerem:
npx @openapitools/openapi-generator-cli generate \ -i api/openapi.yaml -g typescript-axios -o ./sdk/typescript@openapitools/openapi-generator-cli miał 26 września 2026 wersję 2.41.0 w npm; pobiera generator w postaci pliku JAR, więc maszyna potrzebuje środowiska Java. Na granicach usług dodaj testy kontraktowe sterowane przez konsumenta z Pact; ten wzorzec opisuje artykuł mikroserwisy z AI.