API-kontrakter i Norscode

God API-kontrakt sparer tid når teamet vokser. Denne guiden hjelper deg å lage tydelige og forutsigbare endepunkter som andre systemer kan stole på.

En kontrakt er i praksis et løfte mellom systemer. Den forteller hva klienten kan sende, hva den får tilbake, hvilke felter som er stabile, hvordan feil ser ut og hvilke statuskoder som betyr hva. Når denne kontrakten er tydelig, blir integrasjoner, frontend og testing mye enklere. Når den er uklar, må hver klient i praksis gjette seg fram.

Hvordan tenke om kontrakter

Det viktigste er å huske at en API-kontrakt ikke bare er dokumentasjon. Den er en del av selve produktet. Hvis et felt skifter navn, en statuskode plutselig betyr noe annet, eller en feilrespons varierer fra rute til rute, påvirker det andre systemer direkte. Derfor bør kontraktstenkning være like viktig som intern kodekvalitet.

En god kontrakt er lett å lese, lett å teste og stabil nok til at klienter kan bygge rundt den uten frykt for overraskelser i hver release.

1) Standard svarformat

Lag et fast JSON-format for suksess og feil. Da blir frontend, klienter og logging mye enklere.

funksjon api_ok(data: tekst) -> tekst {
    returner "{\"ok\":true,\"data\":" + data + "}"
}

funksjon api_error(melding: tekst, kode: heltall) -> tekst {
    returner "{\"ok\":false,\"code\":" + tekst_fra_heltall(kode) + ",\"melding\":\"" + melding + "\"}"
}

Et standardformat gjør at klienten slipper å lære en ny responsform for hver rute. Det er spesielt nyttig når flere utviklere jobber på samme API eller når både frontend, integrasjoner og supportverktøy bruker de samme endepunktene.

Det viktigste er ikke nøyaktig hvilke felter du velger, men at mønsteret er konsekvent. Hvis suksess og feil ser helt forskjellige ut mellom ruter, blir klientlogikk og testarbeid unødvendig tungt.

2) Statuskoder konsekvent

2xx

Gyldige forespørsler med forventet resultat.

4xx

Klientfeil: manglende felt, ugyldig input, ugyldig token.

5xx

Serverfeil: uforutsette feil, avhengighetsfeil, tidsavbrudd.

429

Rate-limit eller for mange samtidige kall.

Statuskoder er en del av kontrakten, ikke bare tekniske detaljer. Hvis klientfeil noen ganger returnerer 200 og andre ganger 400, blir API-et vanskeligere å tolke maskinelt. Hvis interne feil pakkes som 4xx, risikerer klienten å håndtere serverproblem som om brukeren gjorde noe galt.

Konsekvent bruk av statuskoder gjør også observability bedre. Feilrate, dashboards og varsler blir mye mer nyttige når kodene faktisk betyr det de burde.

3) Payload-navn og datatyper

Bruk konsekvent språk i nøkkelnavn; unngå blanding av id, Id eller norske og engelske varianter.

test "payload-format" {
    la payload: tekst = "{\"ok\":true,\"data\":{\"id\":\"KON-1\",\"navn\":\"Norscode\"}}"
    assert(tekst_inneholder(payload, "\"ok\":true"))
    assert(tekst_inneholder(payload, "\"navn\":\"Norscode\""))
}

Små inkonsistenser i feltnavn og typer er en vanlig kilde til friksjon. Et API kan fungere, men likevel være irriterende og sårbart å bruke hvis samme type ressurs beskrives litt ulikt fra ett endepunkt til et annet.

Det hjelper å velge ett språk og ett navnemønster tidlig. Det samme gjelder datatyper. Hvis et felt er tekst i ett svar og tall i et annet, skapes unødvendig kompleksitet i klientene selv om begge svarene virker “logiske” isolert sett.

4) Feil skal være maskinlesbare

Legg felter på feil som gjør det mulig å vise melding til bruker og samtidig loggføre årsak.

funksjon feil_respons(felt: tekst, melding: tekst) -> tekst {
    returner "{"
      + "\"ok\":false,"
      + "\"felt\":\"" + felt + "\","
      + "\"melding\":\"" + melding + "\""
      + "}"
}
Ikke gi en lang tekst som eneste feilfelt. Del opp i kode, felt og lesbar melding.

Maskinlesbare feil er nyttige fordi de lar klienten ta intelligente valg. Et felt kan markeres i et skjema, en retry kan stoppes, eller en spesifikk melding kan vises uten at klienten må tolke fri tekst. Jo tydeligere feilstrukturen er, jo mindre “if this string contains …” får du i frontend og integrasjoner.

Det er også lettere å logge og analysere feil når de har tydelige koder og felt. Da blir feilhåndtering en stabil del av kontrakten i stedet for noe som varierer med utviklerstil.

5) Kontrakter beskytter også intern kode

API-kontrakter er ikke bare for eksterne klienter. De beskytter også arkitekturen internt. Når grensene mellom handler-lag, domene og integrasjoner er tydelige, blir det lettere å vite hva som skal oversettes hvor, og hva som faktisk er offentlig format versus intern modell.

Dette er spesielt viktig når flere systemer eller team skal bruke samme tjeneste. En god kontrakt reduserer behovet for muntlige avklaringer og gjør at flere kan jobbe parallelt uten å skape skjulte avhengigheter.

6) Paging for liste-endepunkter

Store lister bør være paginerte fra start.

funksjon paginer_utvalg(total: heltall, side: heltall, størrelse: heltall) -> tekst {
    hvis side <= 0 da { returner "0" }
    hvis størrelse <= 0 da { returner "0" }
    returner tekst_fra_heltall((side - 1) * størrelse)
}

Paging er en kontraktsbeslutning, ikke bare en ytelsesdetalj. Hvis liste-endepunkter starter som “returner alt” og senere må endres, blir det fort en breaking change for klienter som bygget seg rundt gammel oppførsel.

Ved å tenke paging tidlig, gjør du både ytelse og klientbruk mer forutsigbar. Klienten vet hvordan større datamengder skal hentes, og serveren slipper å endre grunnformen senere under press.

7) Versjonering og bakoverkompatibilitet

Bruk versjon i URL eller header. Når du endrer obligatoriske felter, vurder ny versjon i stedet for breaking change.

Versjonering handler om å gi klienter tid til å tilpasse seg. Hvis et API endres for raskt eller uten tydelig signal, blir selv små forbedringer til brudd i andre systemer. Det er sjelden et teknisk problem alene; det blir fort også et samarbeidsproblem.

Bakoverkompatibilitet er derfor ofte billigere enn mange tror. Å innføre nye felt gradvis og holde gamle kontrakter stabile litt lenger gir ofte langt mindre totalarbeid enn å tvinge alle klienter over på én gang.

8) Kontraktsjekkliste før release

Sjekk denne listen før du publiserer et nytt API:

  1. OpenAPI inneholder alle nye endepunkter.
  2. Feilformat er testet i både suksess- og feilsituasjon.
  3. Statuskoder og feiltekster er stabile.
  4. Ingen nye breaking changes uten versjon.
God praksis: legg testsett som validerer kontrakten, ikke bare funksjonsresultatet.

Denne kontrollen er nyttig fordi kontraktsbrudd ofte ser små ut i koden, men store ut for klienten. En liten feltendring, annen casing eller ny statuskode kan være nok til å stoppe en frontend, en mobilapp eller en partnerintegrasjon.

9) Hva som ofte bryter kontraktskvalitet

Slike problemer kommer ofte ikke av dårlige intensjoner. De kommer av fart. Derfor er det nyttig å ha kontraktstenkning som en egen disiplin i prosjektet, ikke bare som en ettertanke når frontend eller integrasjoner begynner å klage.

10) Hva du bør ta med deg videre

Den viktigste lærdommen er at kontrakter skaper ro mellom systemer. Når felter, statuskoder, feilformat og versjoner er tydelige, kan klienter, tester og team bygge videre med langt mindre usikkerhet.

Et stabilt API er derfor ikke bare et teknisk mål. Det er en måte å redusere friksjon i hele leveransen