Categorie
Kennisbank
Onderwerp
API & integraties
Leestijd
16 minuten
Niveau
Tech-lead
Bijgewerkt
Mei 2026

API-integratie best practices.

Een praktische gids voor technisch-betrokken lezers — product owners, tech-leads en integration-engineers — die niet "wat is een API" willen weten, maar willen weten hoe je een koppeling bouwt die ook over twee jaar nog overeind staat. Authenticatie, idempotency, retries, observability, schema-versioning en de AVG-laag die er bovenop hoort.

Waarom een aparte gids voor best practices?

De basisvraag "wat is een API-integratie" beantwoorden we in onze gids over wat een API-integratie is. Deze pagina is voor de fase erna: u weet inmiddels dat u een koppeling gaat bouwen, of in beheer hebt, en wilt weten welke patronen het verschil maken tussen een integratie die werkt op de demo en een integratie die overeind blijft onder productie-druk — met vendor-uitval, piek-loads en een AVG-audit die volgend kwartaal langskomt.

Veel teams beginnen aan een koppeling met de mindset "het is een HTTP-call, dat krijgen we wel werkend". Dat klopt voor het prototype. Het klopt minder zodra de eerste retry-storm tegen de rate limit van een vendor aanloopt, of zodra blijkt dat dezelfde betalingsbevestiging twee keer is doorgezet omdat een webhook is herhaald. De best practices hieronder zijn de defensieve laag die productiekwaliteit van demo-kwaliteit onderscheidt. Wilt u eerst weten wat een API-integratie kost en welke architectuurkeuzes dat beïnvloeden, begin daar; voor de afweging custom-versus-platform zie API versus integratieplatform.

i
Hoe te lezen

De secties lopen van "buiten naar binnen" — eerst patronen over de netwerkrand (auth, retries, webhooks), dan over uw eigen architectuur (observability, gateways, BFF), tenslotte de governance-laag (versioning, secrets, AVG).

Authenticatie: OAuth2, JWT en mTLS

Authenticatie is het deel van een integratie waar veel kostbare tijd in verdwijnt. Niet omdat het inhoudelijk ingewikkeld is, maar omdat de aannames van twee systemen over hoe een aanroeper zich identificeert zelden goed op elkaar aansluiten. Drie patronen die u in de praktijk tegenkomt — vaak in combinatie.

OAuth2 voor klant- en SaaS-flows

OAuth2 is de defacto-standaard voor scenario's waarbij een gebruiker een derde partij toestemming geeft om namens hem data te benaderen. De client_credentials-grant is voor server-to-server, authorization_code voor user-flows met een redirect. Leg vooraf vast welke scopes u aanvraagt, hoe lang refresh-tokens leven, en wat uw plan is als een token tijdens een nachtelijke batch verloopt. Veel teams ontdekken pas in productie dat een refresh-token na lange inactiviteit ongeldig wordt — en hebben dan geen geautomatiseerd herstel-pad.

JWT voor service-to-service

JSON Web Tokens met asymmetrische ondertekening passen bij scenario's waarbij u zelf het identity-domein beheert: een microservice belt een andere met een token dat met een private key is ondertekend, de ontvanger valideert met de publieke sleutel. Korte expiry, beperkte scopes per token, key-rotation als vast ritueel, en een ondubbelzinnige aud-claim zodat tokens niet hergebruikt kunnen worden tegen een ander endpoint.

mTLS voor B2B en enterprise-koppelingen

Bij koppelingen met banken, verzekeraars, zorginstellingen of overheid komt mutual TLS regelmatig voor: beide kanten van de verbinding tonen een certificaat. Het verhoogt de drempel voor een aanvaller aanzienlijk, maar introduceert nieuwe operationele complexiteit — certificaten verlopen, en doen dat altijd op het slechtst denkbare moment. Bouw certificate-monitoring in als first-class concern, en behandel het verlengen als een proces met testomgeving-eerst, niet als handwerk in productie.

Wat u expliciet niet wilt

API-keys in URL-parameters (loggen door iedereen die toegang heeft tot access-logs), basic-auth zonder TLS, gedeelde service-accounts waarvan niemand meer weet wie ze gebruikt, en custom auth-protocollen. Voor iets fundamenteels als authenticatie loont het zelden om een eigen recept te koken — gebruik bewezen patronen, en investeer de tijd liever in observability rond auth-falen.

Idempotency-keys: het patroon dat het meeste leed scheelt

Een operatie heet idempotent als hem twee keer uitvoeren hetzelfde resultaat oplevert als hem één keer uitvoeren. Voor leesacties (GET) geldt dat van nature. Voor schrijfacties (POST, PUT, PATCH) moet u het bewust ontwerpen. Het mechanisme: bij elke schrijfactie genereert de aanroeper een unieke Idempotency-Key (een UUID volstaat) en stuurt die mee als header. De server slaat deze key tijdelijk op met het resultaat van de eerste succesvolle aanroep. Komt dezelfde key binnen een venster opnieuw binnen — bijvoorbeeld omdat een netwerk-timeout een retry triggerde terwijl de oorspronkelijke aanvraag wel slaagde — dan geeft de server het opgeslagen resultaat terug zonder de operatie nogmaals uit te voeren.

POST /v1/payments
Idempotency-Key: 9f4c2b0e-13aa-4f3a-8e6b-a5e0c3b1d8c2

{ "amount": 2500, "currency": "EUR", "order_id": "ord_8731" }

Belangrijke detail-keuzes: hoe lang houdt u keys vast, wat doet u bij zelfde key + andere body (een 409 is netter dan stilletjes negeren), en hoe communiceert u in uw API-docs welke endpoints deze header accepteren. Bouwt u zelf een API die door anderen wordt geconsumeerd? Maak idempotency niet optioneel — maak hem default, en valideer dat de header aanwezig is bij alle niet-GET-acties.

Retry-strategie: exponential backoff met jitter

Netwerken falen, vendors zijn even bezig, deploys lopen door. Een goed gebouwde integratie probeert het opnieuw — maar niet als een papegaai. Drie principes die samen werken:

  • Exponential backoff: wacht na de eerste mislukking bijvoorbeeld 1 seconde, dan 2, dan 4, dan 8. U gunt het andere systeem ademruimte in plaats van het te overspoelen.
  • Jitter erbovenop: zonder een willekeurige variatie op de wachttijd krijgt u "thundering herd" — tien clients die op exact hetzelfde moment opnieuw proberen na een vendor-uitval, met een tweede zelf-geïnduceerde uitval als gevolg.
  • Een retry-budget: definieer een max aantal pogingen of een tijdsbudget en leg daarna neer in een dead-letter queue. Een retry-loop die uren doordraait zonder dat iemand het ziet, is wat u absoluut niet wilt.
def retry_with_backoff(call, max_attempts=6):
    delay = 1.0
    for attempt in range(max_attempts):
        try: return call()
        except RetryableError:
            if attempt == max_attempts - 1: raise
            time.sleep(delay * (0.5 + random.random()))
            delay *= 2

Niet elke fout is retryable. Een 401 of 400 retryen lost niets op — dat zijn permanente fouten. Een 429, 502/503/504 of network-timeout zijn klassieke retryable cases. Codeer dit verschil expliciet. Voor de bredere aanpak rond robuuste koppelingen zie onze dienst slimme API-integraties.

Circuit breakers: stop met aankloppen als de deur dicht is

Als een dependency volledig uitvalt, is doorgaan met retries soms erger dan stoppen. Een circuit-breaker bewaakt het succespercentage van een endpoint over een rollend venster. Zakt dat onder een drempel, dan "opent" de breaker: alle volgende calls falen direct, zonder dat de vendor nog wordt belast. Na een afkoelperiode probeert de breaker een half-open state met enkele kalibratie-calls; lukken die, dan sluit hij weer.

Het voordeel is tweeledig: uw eigen systeem blijft responsief, en u helpt de vendor herstellen door geen druk meer toe te voegen. Bibliotheken als Resilience4j (Java), Polly (.NET) of een eigen wrapper rond de HTTP-client maken het patroon eenvoudig in te bouwen. Pas op met te lage drempels in low-volume situaties: als een endpoint maar weinig aanroepen krijgt, geeft één fout snel een hoge failure-rate en opent de breaker te snel. Stem venster en drempel af op uw verkeer.

Rate limits respecteren — en proactief verspreiden

Bijna elke moderne API kent rate limits. Goed ermee omgaan is een kwestie van twee dingen: reageren op de signalen die de vendor geeft, en proactief het werk verspreiden.

  • Honoreer limit-headers: bij een 429 of soms al bij een 200 sturen vendors X-RateLimit-Remaining, X-RateLimit-Reset en Retry-After. Bouw uw retry-policy zo dat hij eerst Retry-After volgt en pas terugvalt op exponential backoff als die header ontbreekt.
  • Spreid batches met een token-bucket: voor grote import-jobs is een naïeve loop de garantie voor 429-storms. Een token-bucket voor uw API-client beperkt zelf de outbound-rate tot wat de vendor verdraagt. Batches die "te traag" lijken op één thread zijn op het einde vaak sneller klaar dan een burst die continu wordt afgebroken.
  • Onderhandel zo nodig: veel SaaS-vendors verhogen rate limits op redelijk verzoek voor enterprise-contracten. Voor een initiële historische sync vraagt u beter vooraf om verruiming dan dat u uw productie-koppeling tegen de wand laat lopen.

Webhooks versus polling: kies bewust per stroom

Voor wijzigingen ophalen heeft u grofweg twee opties: pollen of een webhook ontvangen. Bij zeldzame events die snel verwerkt moeten worden (betalingen, fraude-meldingen, disputes) zijn webhooks beter — polling verspilt capaciteit. Polling wint wanneer u replay-controle wilt, zelf het venster bepaalt, of de vendor geen betrouwbare webhook-delivery biedt. Een uurlijkse incrementele pull op een updated_at-filter is vaak robuuster dan een webhook waarvan u nooit zeker weet of hij is aangekomen.

Wat een productiewaardige webhook-handler doet

  • Signatures verifiëren: elke serieuze vendor ondertekent webhooks met HMAC. Verifieer voordat u de body ook maar parset, anders bent u open voor spoofing.
  • Snel antwoorden, asynchroon verwerken: beantwoord binnen seconden met 2xx, zet de payload in een queue, verwerk in een aparte worker. Een trage handler veroorzaakt retries aan vendor-kant.
  • Idempotent verwerken: gebruik de event-ID van de vendor als idempotency-key, zodat dezelfde event maar één effect heeft.
  • Niet-verwerkbare events bewaren: faalt verwerking, breng het event naar een dead-letter queue met genoeg context voor handmatig of geautomatiseerd herstel.

De meeste robuuste koppelingen combineren beide: webhooks voor near-realtime signalen, een periodieke polling-reconciliation om gemiste events op te sporen. Wie alleen op webhooks vertrouwt ontdekt vroeg of laat dat hij iets gemist heeft.

Eventual consistency: omarm het, of betaal de prijs

Twee systemen die elkaar via APIs synchroniseren zijn niet realtime gelijk. Er zit altijd vertraging op — milliseconden in het beste geval, minuten of langer als batch-verwerking erbij zit. Die vertraging ontkennen leidt tot bugs die zich uitsluitend bij hoge load manifesteren.

Twee patronen die helpen. Ten eerste: vermijd vragen als "kun je dit record onmiddellijk opvragen na de aanmaak?". Vervang ze door event-driven flows waarbij het ontvangende systeem zelf wacht op een bevestiging dat het bericht is verwerkt. Ten tweede: behandel state-conflicten expliciet. Last-write-wins is een acceptabele strategie als u het bewust kiest; problematisch als hij sluipenderwijs ontstaat. Voor records met meerdere bronnen kunt u beter werken met version-vectors, CRDT's of een leidend-systeem-per-veld-afspraak.

Wat absoluut helpt: timestamps consequent in UTC opslaan, geen wall-clock-ordering vertrouwen tussen distributed systems, en kritieke state bewaken met checksums of reconciliation-jobs die periodiek systeem A en B met elkaar vergelijken.

Observability: correlation-IDs, structured logging, traces

Wanneer een storing zich voordoet over twee of drie systemen, is uw mogelijkheid om hem te diagnostiseren bijna volledig afhankelijk van wat u tijdens de bouw aan observability heeft ingebouwd. Achteraf toevoegen is technisch mogelijk maar pijnlijk.

  • Correlation-IDs door de hele keten: genereer een unieke X-Request-ID (of traceparent) bij de start van elke flow en geef die door aan elke downstream-call. Zonder dit patroon is integratie-debugging een leesoefening in twintig verschillende logs op zoek naar tijdstempel-overlap.
  • Structured logging: log in JSON met consistente veldnamen — level, service, request_id, action, resource, duration_ms, outcome. Plaintext-logs zijn onbruikbaar zodra u ze moet aggregeren over services.
  • Distributed tracing: OpenTelemetry is de standaard. Spans rond elke API-call geven een visueel beeld van waar de tijd in een request blijft — vaak veel sneller dan loggen alleen.
  • Metrics die ertoe doen: success-ratio per endpoint (niet over alles gemiddeld), P95/P99-latency in plaats van gemiddelden, queue-diepte voor async flows, en dead-letter-queue-volume als business-metric.
i
Vuistregel

Als u niet binnen vijf minuten kunt vaststellen of een specifieke transactie is geslaagd, wat er onderweg is gebeurd en welk systeem de fout veroorzaakte, is uw observability nog niet productiewaardig.

Schema-versioning: aan beide kanten van de koppeling

Een schema is geen monument; het evolueert. Het verschil tussen een koppeling die soepel meebeweegt en een koppeling die jaarlijks "opgepoetst" moet worden, zit in hoe u versioning vanaf dag één behandelt.

Aan de vendor-kant: pin uw integratie op een specifieke API-versie. Een client die "v1" zegt en impliciet de laatste minor krijgt, raakt soms onderweg gebroken zonder dat iemand een release deed. Lees changelog-announcements en plan deprecations in als regulier werk.

Aan uw eigen API-kant: versioneer expliciet — in de URL (/v1/orders), in een header (Accept: application/vnd.appfront.v1+json) of via een query-parameter. Maak backwards-incompatible changes alleen onder een nieuwe versie, en geef oude versies een gedocumenteerde sunset-datum.

Bewust additief versus breaking: velden toevoegen is bijna altijd veilig mits consumers schema-tolerant zijn gebouwd. Velden weghalen, type veranderen of validatie strenger maken is altijd breaking en hoort in een versie-bump — voor de consumer is een silently-strenger-geworden veld nauwelijks van een bug te onderscheiden.

Contract-testing: zelfde taal aan beide kanten

Een eenheidstest verifieert dat uw code doet wat u denkt. Een contract-test verifieert dat uw integratie doet wat de andere partij verwacht — en omgekeerd. Tools als Pact zijn hiervoor gemaakt: de consumer beschrijft welke calls hij doet en welke responses hij verwacht; de provider verifieert dat zijn werkelijke gedrag overeenkomt. Dit rendeert vooral als u én consumer- én provider-rol binnen dezelfde organisatie heeft. Voor externe-vendor-koppelingen volstaan vaak integratie-tests tegen een sandbox, met regelmatige re-runs om regressie aan vendor-kant op te sporen. Combineer mocked-tests in CI met geplande sandbox-runs voor echte verificatie.

Error-handling en dead-letter queues

Wat doet u met een bericht dat ondanks retries blijft falen? "Oneindig opnieuw proberen" is fout, "weggooien" is fouter. Het juiste antwoord is een dead-letter queue (DLQ): een aparte bewaarplek voor berichten die uw verwerking niet aankan, met genoeg context om handmatig of geautomatiseerd hersteld te worden. Categoriseer fouten daarbij: transient (netwerk-glitch, vendor-uitval) gaat in retry met backoff, permanent (4xx behalve 429) gaat direct naar DLQ zonder retries, onbekend (5xx, vage timeouts) gaat in retry tot het budget op is en dan naar DLQ.

In een DLQ horen de originele payload, alle gefaalde pogingen met error-details, tijdstip, correlation-ID, en cruciaal: een mechanisme om het bericht na correctie terug te zetten in de hoofdstroom. Een DLQ zonder replay-pad wordt een digitaal kerkhof. Een DLQ die plotseling groeit is altijd een signaal dat iemand binnen het uur moet zien — definieer thresholds en alerts, en behandel DLQ-leegmaken als regulier werk in plaats van crisisreactie.

Secrets management en security

API-keys, OAuth-client-secrets, certificate-private-keys, database-credentials — een integratie-laag is een fabriek vol secrets. Vier regels:

  • Een centrale secret-store: geen secrets in code, geen secrets in environment-files die in Git terechtkomen. Gebruik HashiCorp Vault, AWS Secrets Manager / KMS, Azure Key Vault, GCP Secret Manager of vergelijkbaar — met short-lived credentials en audit-log.
  • Rotatie als regel, niet als incident: automatiseer rotation in plaats van handwerk-na-lek. Refresh-token-rotation voor OAuth, vendor-specifieke flows voor API-keys, monitoring + automatische verlening voor certificaten.
  • Least-privilege: integratie-accounts mogen alleen wat ze moeten. Een sync-account voor productdata heeft geen rechten op klantgegevens. Een lek in één integratie wordt zo geen lek in uw hele systeemlandschap.
  • Geheimen niet in logs: een serializer die de hele request inclusief Authorization-header in een error-bericht zet, lekt secrets naar uw observability-stack. Bouw redaction in als first-class concern.

Voor de bredere security-context zie ons informatiebeveiligingsbeleid voor de stance die wij volgen bij projecten waar dit ertoe doet.

API-gateway en backend-for-frontend

Bij meer dan een handvol koppelingen loont het om toegangspatronen te centraliseren. Een API-gateway zit tussen consumers en uw backends en handelt cross-cutting concerns af: authenticatie en token-validatie, rate-limiting per client, request-logging, caching, transformatie. Voorbeelden: Kong, Apigee, AWS API Gateway, Tyk, of zelfbouw rond Envoy. U bouwt deze logica niet in iedere microservice opnieuw — maar pas op dat de gateway zelf geen kritiek single-point-of-failure wordt.

Een backend-for-frontend (BFF) is een lichte laag die specifiek voor één type frontend (web, mobiel, partner-portaal) data uit meerdere backends aggregeert. Het voorkomt complexe orkestraties in de frontend, en voorkomt dat uw microservices vol komen met frontend-specifieke endpoints. Bij frontend-zware producten waar u meerdere systeemlandschappen consolideert is dit vrijwel altijd de moeite waard — onze pagina over integraties binnen web-ontwikkeling gaat er dieper op in.

GraphQL versus REST: het hangt af van uw vorm

REST wint vaak bij goed gedefinieerde resources met heldere CRUD-semantiek, caching aan de edge (REST is HTTP-native, GraphQL minder), publieke API's voor een breed publiek dat niet wil leren over schemas, en scenario's waar u idempotency en versioning per endpoint expliciet wilt vormgeven.

GraphQL wint vaak bij frontend-zware applicaties die data uit veel bronnen aggregeren met onder-/overfetching-problemen, snel evoluerende client-behoeftes, en interne API's waar consumers en providers in hetzelfde team zitten. Let op valkuilen: N+1-queries vragen om dataloaders, query-complexiteit moet ingeperkt via cost-analysis of allowlists, en caching is geen out-of-the-box HTTP-cache meer. De pragmatische keuze in veel organisaties: REST voor publieke integratie-API's, GraphQL voor de BFF-laag tussen interne frontends en backends.

De AVG-laag: data-minimization, EU-residency, redaction, audit-trails

Een API-integratie die persoonsgegevens vervoert valt onder de AVG. Geen laagje verf na de bouw — een set ontwerp-eisen die door de architectuur heen lopen.

  • Data-minimization in payloads: stuur alleen wat de ontvanger werkelijk nodig heeft. Een marketing-tool heeft geen BSN nodig, een logistieke partner geen geboortedatum. Definieer per integratie een expliciete projectie van de bron-record — geen blob-doorgifte.
  • EU-residency van endpoints: voor AVG-gevoelige data is de fysieke locatie van de verwerker materieel. Controleer per vendor of EU-hosting beschikbaar is, kies daar bewust voor, en documenteer in uw verwerkingsregister.
  • PII-redaction in logs: een log-regel met email=jansen@example.com order=1234 status=failed is een AVG-incident in wachtstand zodra die log-stream wordt benaderd. Lokaliseer PII-velden, redact of hash ze, en behandel error-bodies met dezelfde zorg als payload-bodies.
  • Audit-trails: immutable, append-only, met genoeg context om individuele acties terug te vinden, en een bewaartermijn die past bij uw verwerkersregister. Voor zorg komt NEN 7510 erbij, voor finance DORA.

De fouten die wij telkens weer zien

Tot slot een verzameling antipatronen die we in de praktijk regelmatig tegenkomen — niet als oordeel, maar als checklist.

  • Niet-idempotente schrijfacties zonder vangnet: een betalingsbevestiging die twee keer wordt verwerkt, een order die dubbel binnenkomt. In bijna elk geval te voorkomen met idempotency-keys.
  • Retry-storms: een vendor heeft drie minuten downtime, uw 200 nodes proberen tegelijk opnieuw, de vendor heeft nu uren downtime. Jitter, breakers en retry-budgets bestaan niet voor de mooie sier.
  • Hidden coupling: systeem B vertrouwt erop dat systeem A altijd snel antwoordt, zonder dat dit in een contract staat. Als A's latency verschuift, breekt B — onzichtbaar tot het breekt.
  • Versioning-deadlocks: u wilt naar een nieuwe API-versie, de vendor heeft de oude weggehaald, consumers zitten nog op de oude — en niemand voelt zich eigenaar van de migratie. Plan versie-transities vooraf met een sunset-datum.
  • Missing observability tot de eerste storing: u merkt dat een koppeling stuk is omdat een klant belt. Toen die telefoon nodig was, had u alerts moeten hebben.
  • Security via obscurity: een endpoint dat "niemand kan vinden omdat de URL niet gedocumenteerd is". Scanners vinden hem snel genoeg. Auth en authorisatie zijn niet onderhandelbaar, ook niet intern.
  • Lock-in op vendor-specifieke features: een handige proprietaire feature, een jaar later wilt u migreren, en die feature bestaat nergens anders. Houd in architectuur-documentatie bij welke vendor-aannames u accepteert en wat exit zou kosten.
!
Tot slot

Een API-integratie is geen losse HTTP-POST — het is een verzameling afspraken tussen twee systemen die samen onder productie-druk moeten blijven werken. De best practices hierboven zijn de verzameling van die afspraken in defensieve vorm. Geen ervan is exotisch, bijna geen ervan is duur. Het verschil tussen integraties die staan en integraties die telkens omvallen zit in welke u serieus genoeg neemt om vóór go-live in te bouwen.

De drie kernpunten.

01

Defensief bouwen vanaf dag één

Idempotency, retries met jitter, circuit-breakers, dead-letter queues en signature-validatie zijn geen luxe maar de basislaag van een productiekoppeling.

02

Observability is geen achteraf-werk

Correlation-IDs, structured logging, traces en metrics moeten in het ontwerp zitten — niet erna ingebouwd. Anders bent u blind in de eerste vendor-uitval.

03

AVG en security zitten in het ontwerp

Data-minimization, EU-residency, PII-redaction in logs en least-privilege voor service-accounts zijn architectuur-eisen, geen losse compliance-laag.

Een koppeling die productie-druk aankan?

Een kennismaking van een half uur. We luisteren naar welke koppelingen u in beheer heeft of wilt bouwen, waar de pijn zit en welke patronen het meest zouden helpen — en geven dan een eerlijke richting voor scope, aanpak en aanpakroute.

Edit Content