Przejdź do głównej zawartości

Tworzenie 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.

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.

ArtefaktKto sprawdzaJak
Kontrakt (api/openapi.yaml, schema.graphql lub .proto)Ty, linia po linii; właściciel API zatwierdza przez CODEOWNERSPull request zmieniający wyłącznie kontrakt
Styl i poprawność kontraktuMaszynaLint Spectral lub Redocly, buf lint, walidacja schematu GraphQL
Zgodność z konsumentamiMaszynaoasdiff breaking, buf breaking, graphql-inspector diff względem main
Handlery, walidacja, zapytaniaMaszynaSprawdzanie typów, testy jednostkowe i integracyjne
Zgodność handlerów z kontraktemMaszynaSchemathesis na uruchomionym serwerze
Opóźnienie i liczba zapytań pod obciążeniemMaszynak6 z progami, które kończą przebieg błędem
Ścieżki autoryzacji, pieniędzy i usuwania danychAgent 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.

  1. Naszkicuj kontrakt z agentem. Poproś wyłącznie o specyfikację, z wpisaną paginacją, autoryzacją, formatem błędów i wzorcami dostępu. Bez kodu serwera.

  2. Przepuść go przez linter i przejrzyj osobiście. Uruchom linter kontraktu, potem przeczytaj diff. To jedyny artefakt, który czytasz w całości.

  3. Scal kontrakt osobno. Pull request z kontraktem zmienia tylko plik kontraktu. Gałęzie implementacyjne traktują go jako tylko do odczytu.

  4. 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.

  5. 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.

  6. Sprawdź zgodność wsteczną i wygeneruj klientów. Diff zmian łamiących względem main blokuje 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.

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ł:

Okno terminala
# Terminal, repo root
echo 'extends: ["spectral:oas"]' > .spectral.yaml
npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn

Ta sama dyscyplina dotyczy innych protokołów. Zmieniają się tylko narzędzia:

ProtokółKontraktLintKontrola zmian łamiących
RESTapi/openapi.yaml (OpenAPI 3.1)npx @stoplight/spectral-cli lint lub npx @redocly/cli lintoasdiff breaking --fail-on ERR base.yaml api/openapi.yaml
GraphQLschema.graphql z typami connection dla listnpx @graphql-inspector/cli validate 'src/**/*.graphql' schema.graphql (sprawdza każdą operację względem schematu)npx @graphql-inspector/cli diff old.graphql schema.graphql
gRPCPliki .proto z field mask dla częściowych aktualizacjinpx @bufbuild/buf lintnpx @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.

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.

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.

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.

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:

Okno terminala
# Terminal, repo root: ${STAGING_DATABASE_URL} is expanded from your shell at launch
claude mcp add -s project postgres -e 'DATABASE_URI=${STAGING_DATABASE_URL}' \
-- uvx postgres-mcp --access-mode=restricted

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.

BramkaCo łapiePolecenie
Lint kontraktuBrakujące odpowiedzi błędów, niespójne nazwy, nieudokumentowane operacjenpx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
Typy i testyZłe kody statusu, zepsutą walidację, paginację, w której przekazany z powrotem nextCursor nie zwraca kolejnej stronynpm run typecheck && npm test
Test pokrycia autoryzacjąTrasę zarejestrowaną bez requireAuthtests/auth-coverage.test.ts (prompt niżej)
Fuzzing kontraktowyOdpowiedzi 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ącychUsunięte pola, nowe wymagane parametryoasdiff breaking --fail-on ERR base.yaml api/openapi.yaml
Progi obciążenioweZapytania N+1, wolne endpointy listującek6 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 pipefail
BASE_URL="${BASE_URL:-http://localhost:3000}"
npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
npm run typecheck
npm 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.yaml
else
echo "::warning::no base contract on main, breaking-change check skipped"
fi
curl -sf "$BASE_URL/health" > /dev/null
uvx schemathesis run api/openapi.yaml --url "$BASE_URL" --checks all \
--header "Authorization: Bearer ${TEST_API_TOKEN:?}" \
--report junit --report-dir reports/schemathesis

Zadaniem 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.

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.

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.js
import 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.

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:

Okno terminala
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.