Fouten

Er zijn twee soorten fouten, en ze vragen om verschillende dingen van je code. Iets mis met het verzoek zelf is een programmeerfout of een storing. Iets mis met wat je vroeg is iets dat je aan een gebruiker kunt laten zien.

Fouten in het verzoek

Deze staan in errors op de wortel, met een code in extensions. Er is dan geen data, want er is niets uitgevoerd.

  • Name
    GRAPHQL_PARSE_FAILED
    Type
    200
    Description

    De query is niet te lezen. Een haakje vergeten.

  • Name
    GRAPHQL_VALIDATION_FAILED
    Type
    200
    Description

    De query klopt niet tegen het schema: een veld dat niet bestaat, een argument dat ontbreekt, een verkeerd type.

  • Name
    MAX_COST_EXCEEDED
    Type
    200
    Description

    first of last ligt buiten 1–250.

  • Name
    THROTTLED
    Type
    200
    Description

    De query past niet in je punten. Zie Kosten en limieten.

  • Name
    INTERNAL_SERVER_ERROR
    Type
    500
    Description

    Het ging bij ons mis. Probeer het opnieuw; blijft het gebeuren, mail ons het X-Request-Id uit het antwoord.

Veld bestaat niet

{
  "errors": [
    {
      "message": "Cannot query field \"kleur\" on type \"Theme\".",
      "extensions": { "code": "GRAPHQL_VALIDATION_FAILED" }
    }
  ]
}

Te duur

{
  "errors": [
    {
      "message": "Throttled: deze query kost 1002 punten en er zijn er 1000 beschikbaar.",
      "extensions": { "code": "THROTTLED" }
    }
  ],
  "extensions": {
    "cost": {
      "requestedQueryCost": 1002,
      "actualQueryCost": null,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 1000,
        "restoreRate": 50
      }
    }
  }
}

Op HTTP-niveau

Een paar dingen worden afgewezen voordat er een GraphQL-antwoord bestaat. Die geven een andere statuscode en één melding als tekst.

  • Name
    400
    Type
    Bad Request
    Description

    De body is geen geldige JSON, of er zit geen query in.

  • Name
    401
    Type
    Unauthorized
    Description

    Sleutel ontbreekt, is onbekend of is ingetrokken. Zie Authenticatie.

  • Name
    404
    Type
    Not Found
    Description

    Onbekende API-versie, of een pad dat niet bestaat.

  • Name
    405
    Type
    Method Not Allowed
    Description

    Iets anders dan POST. De Allow-header noemt wat wel kan.

  • Name
    406
    Type
    Not Acceptable
    Description

    Je Accept-header sluit JSON uit. Laat hem weg of sta JSON toe.


Fouten in wat je vroeg

Een dossiernummer dat al bestaat, een datum die niet klopt, een thema dat je na het bestellen nog wilt wijzigen: dat zijn geen programmeerfouten maar uitkomsten. Ze staan in userErrors in de payload van de mutatie, en de HTTP-status is gewoon 200.

Elke melding wijst het pad naar het veld aan, zodat je hem in je eigen formulier op de goede plek kunt tonen.

  • Name
    field
    Type
    [String!]
    Description

    Het pad van buiten naar binnen, bijvoorbeeld ["input", "familyContact", "email"].

  • Name
    message
    Type
    String!
    Description

    Wat er mis is, in het Nederlands.

  • Name
    code
    Type
    enum
    Description

    BLANK, INVALID, TAKEN, NOT_FOUND, IMMUTABLE of UNPROCESSABLE.

Dossiernummer al gebruikt

{
  "data": {
    "cardCreate": {
      "card": null,
      "userErrors": [
        {
          "field": ["input", "externalDossierId"],
          "message": "is al gebruikt voor een andere kaart",
          "code": "TAKEN"
        }
      ]
    }
  }
}

Twee velden tegelijk

{
  "data": {
    "cardCreate": {
      "card": null,
      "userErrors": [
        {
          "field": ["input", "deceased", "dateOfBirth"],
          "message": "moet een datum zijn als JJJJ-MM-DD",
          "code": "INVALID"
        },
        {
          "field": ["input", "familyContact", "email"],
          "message": "moet een geldig e-mailadres zijn",
          "code": "INVALID"
        }
      ]
    }
  }
}

Wat de codes betekenen

  • Name
    BLANK
    Type
    verplicht veld ontbreekt
    Description

    Vul het aan en probeer opnieuw.

  • Name
    INVALID
    Type
    waarde klopt niet
    Description

    Een datum in het verkeerde formaat, een adres dat geen adres is, een id dat nergens naar wijst.

  • Name
    TAKEN
    Type
    al in gebruik
    Description

    Vrijwel altijd een dossiernummer dat al een kaart heeft. Vaak betekent dit dat je verzoek de vorige keer wél is aangekomen; zie idempotency.

  • Name
    NOT_FOUND
    Type
    bestaat niet, of niet voor jou
    Description

    Een kaart van een andere partner bestaat voor jou niet.

  • Name
    IMMUTABLE
    Type
    kan nu niet meer
    Description

    Het thema na het bestellen, bijvoorbeeld. De bestelling hangt aan dat product.

  • Name
    UNPROCESSABLE
    Type
    klopt, maar lukte niet
    Description

    Het verzoek is in orde maar kon niet worden uitgevoerd, bijvoorbeeld een portretfoto op een adres dat niet reageert.


Hoe je hierop programmeert

Drie takken, en je bent er:

  1. errors op de wortel: jouw bug of onze storing. Loggen, melden, niet opnieuw proberen (behalve bij THROTTLED en INTERNAL_SERVER_ERROR).
  2. userErrors gevuld: laat zien aan degene die het formulier invult.
  3. Allebei leeg: gelukt.

Eén plek waar alles langskomt

export async function rouwkaart(query, variables) {
  const antwoord = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'X-Rouwkaart-Access-Token': process.env.ROUWKAART_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({query, variables}),
  })

  if (antwoord.status === 401) {
    throw new Error('Sleutel klopt niet of is ingetrokken')
  }

  const {data, errors, extensions} = await antwoord.json()

  if (errors?.length) {
    const code = errors[0].extensions?.code
    // Even wachten en opnieuw; de rest is onze of jouw fout.
    if (code === 'THROTTLED') {
      const seconden = Number(antwoord.headers.get('Retry-After') ?? 1)
      await new Promise((r) => setTimeout(r, seconden * 1000))
      return rouwkaart(query, variables)
    }
    throw new Error(`${code}: ${errors[0].message}`)
  }

  return {data, cost: extensions?.cost}
}

Elk antwoord draagt een kenmerk

In de header X-Request-Id staat een uniek id per verzoek. Stuur dat mee als je ons iets vraagt over een verzoek dat misging; dan kunnen wij precies dat verzoek terugvinden in onze logs.

Had je hier iets aan?