# API-first ontwerpen met een OpenAPI-contract

URL: https://brainpowergroup.eu/nl/blog/api-first-ontwerpen
Datum: 2025-09-30
Door: Boris, Brainpower Group

Met een OpenAPI-contract in de repository, linting en een check op breaking changes in CI bouwen frontend en backend in een verspreid team tegelijk.

API-first betekent dat je het contract van een API schrijft en afspreekt voordat iemand hem bouwt. Dat contract is een OpenAPI-bestand in de repository, met een review in een pull request en checks in CI. Een team op meerdere locaties heeft daar twee voordelen van: frontend en backend beginnen tegelijk, en de discussie staat op schrift in plaats van in een call die niet iedereen heeft gehoord.

## Van contract naar implementatie

1. Iemand beschrijft het nieuwe endpoint in het OpenAPI-bestand: het pad, de parameters, de request- en responsebodies, de foutcodes en een voorbeeld per response.
2. Frontend en backend reviewen die wijziging als een gewone pull request.
3. Na de merge bouwt de backend de implementatie en bouwt de frontend tegen een mock.
4. CI controleert of de implementatie bij het contract past.

Zet het contract in dezelfde repository als de backend, dan past een wijziging in code en contract in één pull request. Gebruiken meerdere teams of externe partijen de API, dan werkt een eigen repository met genummerde releases vaak beter.

De nieuwste versie van de specificatie is OpenAPI 3.2, maar niet elke tool leest die al. openapi-typescript noemt bijvoorbeeld alleen 3.0 en 3.1. Blijf op 3.1 tot je hele keten 3.2 aankan.

Voor de developer die het bouwt, is een ticket met "bouw `GET /orders/{id}` volgens het contract" een stuk duidelijker dan "maak een API voor orders". Hoe je zulke tickets schrijft, staat in [tickets die een remote developer begrijpt](/nl/blog/tickets-die-een-remote-developer-begrijpt).

## Waarom het helpt als je niet naast elkaar zit

In een verspreid team is wachten duur. Moet de frontend wachten tot de backend klaar is, dan verlies je dagen. Een vraag die je niet even over je bureau kunt stellen, blijft ook al snel een halve dag liggen.

Met een contract kan de frontend meteen aan de slag. Prism start een mockserver op basis van je OpenAPI-bestand en geeft de voorbeelden terug die je erin hebt gezet. openapi-typescript maakt uit hetzelfde bestand de TypeScript-types. Verandert er een veld in het contract, dan krijgt de frontend bij de volgende build een compileerfout in plaats van een bug in productie.

De discussie over namen, verplichte velden en foutcodes staat in de review van de pull request. Wie later instapt, leest daar terug waarom iets zo is gekozen. Dat past bij [async werken](/nl/blog/async-werken-wat-schrijf-je-op), waarbij beslissingen ook op schrift staan.

## Een contract waar je iets aan hebt

Hier let ik op als ik een contract beoordeel:

- elke response heeft een voorbeeld, ook de foutresponses,
- per veld staat of het verplicht is en of het `null` mag zijn,
- alle endpoints gebruiken hetzelfde foutformaat,
- datums zijn ISO 8601-strings met tijdzone,
- paginering werkt overal op dezelfde manier,
- id's zijn strings, ook als ze nu nog numeriek zijn.

Dit hoef je niet met de hand te controleren. Spectral lint OpenAPI-bestanden met ingebouwde regels, en je voegt eigen regels toe voor je eigen afspraken. Draai het in CI en laat de build falen als het contract een regel breekt.

## Versies zonder verrassingen

Een nieuw endpoint of een nieuw optioneel veld in een response is bijna altijd veilig. Breaking is alles waarmee een bestaande client stukgaat: een veld weghalen of hernoemen, een type wijzigen of een optioneel veld in een request verplicht maken.

oasdiff vergelijkt twee versies van je OpenAPI-bestand en meldt welke wijzigingen bestaande clients breken. Er is een GitHub Action voor, dus je ziet het in de pull request en niet pas als een klant belt.

Moet je toch iets breken, maak dan een nieuwe versie en laat de oude een afgesproken periode draaien. Markeer de oude operaties met `deprecated: true` in het contract. Stuur daarnaast de `Deprecation`-header mee, sinds maart 2025 vastgelegd in RFC 9745, en een `Sunset`-header met de datum waarop de oude versie stopt. Dan ziet ook een team dat je documentatie nooit leest, in de responses dat er iets verandert.

Spreek de lengte van die periode vooraf af met wie de API gebruikt. Voor je eigen frontend die je in dezelfde week uitrolt, is een paar dagen genoeg. Voor apps in de app stores of externe klanten reken je eerder in maanden.

## Contract en code gelijk houden

Het grootste risico is een contract dat langzaam afwijkt van wat de server doet. Twee afspraken helpen daartegen: een pull request die het gedrag van de API verandert, past het contract in dezelfde pull request aan, en de reviewer kijkt naar allebei, zoals beschreven in [code review op afstand](/nl/blog/code-review-op-afstand).

In je testomgeving kun je daarnaast verkeer door de validatieproxy van Prism sturen. Die meldt waar de implementatie afwijkt van het OpenAPI-bestand.

## Beginnen met een bestaande API

1. Zet je bestaande API in een OpenAPI-bestand. Laat het genereren uit je code als dat kan en ruim het daarna met de hand op.
2. Voeg Spectral en oasdiff toe aan je CI.
3. Schrijf het volgende nieuwe endpoint eerst in het contract, in een eigen pull request.
4. Genereer de types voor de frontend uit dat bestand en gooi de handgeschreven types weg.

## Bronnen

- [OpenAPI Specification (laatste versie)](https://spec.openapis.org/oas/latest.html)
- [RFC 9745: The Deprecation HTTP Response Header Field](https://www.rfc-editor.org/rfc/rfc9745.html)
- [Spectral](https://github.com/stoplightio/spectral)
- [oasdiff](https://github.com/oasdiff/oasdiff)
- [Prism](https://github.com/stoplightio/prism)
- [openapi-typescript](https://openapi-ts.dev/)

## Veelgestelde vragen

**Wat betekent API-first ontwikkelen?**

Je schrijft en reviewt eerst het contract van de API, meestal als OpenAPI-bestand, en bouwt daarna de implementatie. Frontend en backend kunnen dan tegelijk beginnen, de frontend tegen een mockserver.

**Welke wijzigingen in een API zijn breaking?**

Alles waarmee een bestaande client stukgaat: een veld weghalen of hernoemen, een type wijzigen of een optioneel veld in een request verplicht maken. Een nieuw endpoint of een nieuw optioneel veld in een response is meestal veilig.

**Hoe laat je weten dat een API-versie verdwijnt?**

Markeer de operaties als deprecated in het OpenAPI-contract, stuur de Deprecation-header uit RFC 9745 mee en zet in een Sunset-header de datum waarop de versie stopt. Laat de oude versie daarna nog een afgesproken periode draaien.

---

Bron: https://brainpowergroup.eu/nl/blog/api-first-ontwerpen
Brainpower Group, contact: https://brainpowergroup.eu/nl/contact, hello@brainpowergroup.eu
