Skip to main content

Foutafhandeling

Deze pagina beschrijft de HTTP statuscodes, foutrespons formaten en best practices voor het afhandelen van fouten in de Flixer API.

HTTP Statuscodes

CodeStatusBeschrijving
200OKHet verzoek is succesvol verwerkt
201CreatedDe resource is succesvol aangemaakt
400Bad RequestHet verzoek bevat ongeldige parameters
401UnauthorizedAuthenticatie is vereist of is mislukt
403ForbiddenDe gebruiker heeft geen toestemming voor deze actie
404Not FoundDe aangevraagde resource bestaat niet
422Unprocessable EntityDe aanvraaggegevens zijn syntactisch correct maar semantisch ongeldig
500Internal Server ErrorEr is een onverwachte serverfout opgetreden

Foutresponse Formaat

Alle API-foutresponsen volgen een gestandaardiseerd formaat:
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Het verzoek bevat ongeldige parameters",
    "details": {
      "field": "email",
      "reason": "Ongeldig e-mailadres"
    },
    "timestamp": "2026-03-27T10:30:45Z",
    "requestId": "req_abc123xyz789"
  }
}
Velden:
  • code — Machine-readable foutcode (bijv. INVALID_REQUEST, UNAUTHORIZED, FORBIDDEN)
  • message — Gebruikersvriendelijke foutbeschrijving in Nederlands
  • details — Aanvullende informatie over de fout (optioneel, vooral voor validatiefouten)
  • timestamp — ISO 8601 tijdstempel van wanneer de fout optrad
  • requestId — Uniek ID voor het verzoek (handig voor debuggen)

Veelvoorkomende Fouten

401 — Unauthorized (Niet Geauthenticeerd)

Oorzaken:
  • Geen geldige Bearer token in de Authorization-header
  • Token is verlopen
  • Token is ongeldig of beschadigd
Oplossingen:
  1. Zorg ervoor dat je een geldige token stuurt in de header:
    Authorization: Bearer YOUR_ACCESS_TOKEN
    
  2. Controleer de vervaldatum van de token
  3. Verkrijg een nieuwe token via het /auth/login endpoint
  4. Implementeer automatische token vernieuwing (zie refresh token flow)

403 — Forbidden (Geen Toestemming)

Oorzaken:
  • De gebruiker is geauthenticeerd maar heeft geen toestemming voor deze actie
  • De gebruiker heeft onvoldoende rollen of permissies
  • De brand/tenant is niet gelinkt aan de gebruiker
Oplossingen:
  1. Controleer of de gebruiker de juiste rol heeft (bijv. admin, technician)
  2. Verifieer dat de geselecteerde brand correct is ingesteld via de X-Request-As-Brand-Members header
  3. Contacteer een administrator om de benodigde permissies toe te wijzen

422 — Unprocessable Entity (Validatiefout)

Oorzaken:
  • Verplichte velden ontbreken
  • Veldwaarden hebben het verkeerde format (bijv. ongeldig e-mailadres)
  • Veldwaarden voldoen niet aan validatieregels
  • Logische validatie is mislukt (bijv. einddatum vóór startdatum)
Oplossingen:
  1. Controleer de error response op de details sectie met veldnamen
  2. Valideer invoergegevens aan clientzijde voordat je ze stuurt
  3. Implementeer client-side validatie met dezelfde regels als de server
  4. Lees de API documentatie voor veldvereisten

500 — Internal Server Error (Serverfout)

Oorzaken:
  • Een onverwachte fout in de servertoepassing
  • Databaseverbindingsfout
  • Externe service is niet beschikbaar
  • Serverresources zijn uitgeput
Oplossingen:
  1. Deze fouten zijn meestal tijdelijk — implementeer retry logic met exponential backoff
  2. Controleer de API status pagina voor bekend onderhoud
  3. Wacht enkele seconden en probeer opnieuw
  4. Als het probleem aanhoudt, stuur het requestId naar de support team
  5. Implementeer fallback logica in je applicatie

Foutafhandeling in Code

JavaScript/TypeScript

Basis foutafhandeling

import axios from 'axios';

interface ApiError {
  error: {
    code: string;
    message: string;
    details?: Record<string, string>;
  };
}

async function fetchData(url: string) {
  try {
    const response = await axios.get(url, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      const apiError = error.response?.data as ApiError;

      if (error.response?.status === 401) {
        console.error('Authenticatie vereist:', apiError.error.message);
        // Redirect naar login of vernieuw token
      } else if (error.response?.status === 403) {
        console.error('Geen toestemming:', apiError.error.message);
        // Toon beperkte toegang bericht
      } else if (error.response?.status === 422) {
        console.error('Validatiefout:', apiError.error.details);
        // Toon validatiefouten aan gebruiker
      } else {
        console.error('API fout:', apiError.error.message);
      }
    }
    throw error;
  }
}

Met token vernieuwing

const apiClient = axios.create({
  baseURL: 'https://api.flixer.com',
});

let accessToken = localStorage.getItem('accessToken');

apiClient.interceptors.request.use((config) => {
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`;
  }
  return config;
});

apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      try {
        const { data } = await axios.post('/auth/refresh', {
          refreshToken: localStorage.getItem('refreshToken'),
        });
        accessToken = data.accessToken;
        localStorage.setItem('accessToken', accessToken);

        // Retry origineel verzoek
        error.config.headers.Authorization = `Bearer ${accessToken}`;
        return apiClient(error.config);
      } catch (refreshError) {
        // Refresh mislukt — redirect naar login
        localStorage.removeItem('accessToken');
        window.location.href = '/sign-in';
      }
    }
    return Promise.reject(error);
  }
);

Python

Basis foutafhandeling

import requests
from typing import Any, Dict, Optional

class FlixerAPIError(Exception):
    def __init__(self, status_code: int, error_data: Dict[str, Any]):
        self.status_code = status_code
        self.error_data = error_data
        super().__init__(error_data.get('error', {}).get('message', 'Unknown error'))

def fetch_data(url: str, access_token: str) -> Dict[str, Any]:
    headers = {
        'Authorization': f'Bearer {access_token}',
    }

    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print('Authenticatie vereist')
            # Vernieuw token of redirect naar login
        elif e.response.status_code == 403:
            print('Geen toestemming voor deze actie')
        elif e.response.status_code == 422:
            error_data = e.response.json()
            print(f"Validatiefout: {error_data['error']['details']}")
        else:
            print(f"API fout: {e.response.text}")

        raise FlixerAPIError(e.response.status_code, e.response.json())
    except requests.exceptions.RequestException as e:
        print(f"Verzoekfout: {str(e)}")
        raise

Met retry strategie

import requests
import time
from typing import Any, Dict, Callable, TypeVar

T = TypeVar('T')

def retry_with_backoff(
    func: Callable[..., T],
    max_retries: int = 3,
    base_delay: float = 1.0,
    backoff_factor: float = 2.0,
) -> T:
    """Voer functie uit met exponential backoff retry."""
    last_exception = None

    for attempt in range(max_retries):
        try:
            return func()
        except requests.exceptions.RequestException as e:
            last_exception = e

            # Niet hergeven bij 4xx fouten (behalve 429 en 408)
            if hasattr(e, 'response') and e.response:
                if 400 <= e.response.status_code < 500:
                    if e.response.status_code not in [429, 408]:
                        raise

            if attempt < max_retries - 1:
                delay = base_delay * (backoff_factor ** attempt)
                print(f"Poging {attempt + 1} mislukt, wacht {delay}s voordat opnieuw geprobeerd wordt...")
                time.sleep(delay)

    raise last_exception

def fetch_with_retry(url: str, access_token: str) -> Dict[str, Any]:
    def request_func():
        headers = {'Authorization': f'Bearer {access_token}'}
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        return response.json()

    return retry_with_backoff(request_func, max_retries=3, base_delay=1.0)

Retry Strategie

Implementeer exponential backoff retry logica voor 5xx fouten en bepaalde 4xx fouten (429, 408):

Exponential Backoff Richtlijnen

Poging 1: Onmiddellijk
Poging 2: Wacht 1 seconde (base_delay)
Poging 3: Wacht 2 seconden (base_delay × 2)
Poging 4: Wacht 4 seconden (base_delay × 4)
...
Formula:
delay = base_delay × (backoff_factor ^ attempt_number)

JavaScript Implementatie

async function fetchWithRetry(
  url: string,
  token: string,
  maxRetries: number = 3,
  baseDelay: number = 1000, // milliseconden
): Promise<any> {
  let lastError: Error | null = null;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
      });

      if (!response.ok) {
        // Niet hergeven bij 4xx (behalve 429 en 408)
        if (response.status >= 400 && response.status < 500) {
          if (![429, 408].includes(response.status)) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
          }
        }
      }

      return await response.json();
    } catch (error) {
      lastError = error instanceof Error ? error : new Error(String(error));

      if (attempt < maxRetries - 1) {
        const delay = baseDelay * Math.pow(2, attempt);
        console.log(`Poging ${attempt + 1} mislukt, wacht ${delay}ms...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }

  throw lastError || new Error('Max retries exceeded');
}

// Gebruik
async function main() {
  try {
    const data = await fetchWithRetry(
      'https://api.flixer.com/customers',
      'your_access_token',
      3, // max retries
      1000, // base delay in ms
    );
    console.log('Succes:', data);
  } catch (error) {
    console.error('Fout na alle pogingen:', error);
  }
}
Waarschuwing: Implementeer retry logica alleen voor idempotente operaties (GET, HEAD, OPTIONS, PUT met dezelfde payload). Wees voorzichtig met POST verzoeken — alleen hergeven als je zeker weet dat het veilig is om te herhalen. Voeg altijd een jitter toe (willekeurige vertraging) tussen pogingen om “thundering herd” problemen te voorkomen.
// Beter: met jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;

Best Practices

  1. Altijd foutafhandeling implementeren — Veronderstel niet dat alle verzoeken slagen
  2. Token vernieuwing automatiseren — Implementeer refresh token logica in je HTTP client
  3. Gebruikersvriendelijke foutmeldingen — Toon gefilterde foutmeldingen aan eindgebruikers (niet alle technische details)
  4. Logging — Log alle API fouten met requestId voor debugging
  5. Exponential backoff — Implementeer retry logica voor transiente fouten (5xx, 429, 408)
  6. Request ID tracering — Bewaar requestId uit foutresponses voor support
  7. Timeouts — Stel altijd HTTP request timeouts in (bijv. 30 seconden)
  8. Idempotency — Gebruik idempotency keys voor POST verzoeken waar mogelijk