Skip to content

Backend API

Volledige API documentatie voor het Fire Management System (FMS) backend. De backend is gebouwd met Node.js, Express, TypeScript en PostgreSQL (met PostGIS voor geografische data).

Inhoud


Overzicht

De FMS API biedt endpoints voor het beheren van natuurbranden in België, inclusief gebruikersbeheer, weerdata, geografische informatie en CSV-imports. De API gebruikt Zod voor request validatie en tsyringe voor dependency injection.

Technische Stack

ComponentTechnologie
RuntimeNode.js
FrameworkExpress 4.x
TaalTypeScript
DatabasePostgreSQL + PostGIS
AuthenticatieJWT (Access + Refresh tokens)
ValidatieZod
DocumentatieSwagger/OpenAPI 3.0
DI Containertsyringe

Base URLs

OmgevingHTTPHTTPS
Developmenthttp://localhost:3000https://localhost:3443
ProductionConfigureerbaar via .envConfigureerbaar via .env

API Versioning

Alle API endpoints gebruiken versioning via het URL pad:

/api/v1/...

Swagger Documentatie

Interactieve API documentatie is beschikbaar op:

http://localhost:3000/docs

Authenticatie

De API gebruikt een JWT-gebaseerd authenticatiesysteem met access en refresh tokens.

Hoe het werkt

  1. Login: Gebruiker logt in met email/wachtwoord → ontvangt access token + refresh token (in httpOnly cookie)
  2. API Requests: Access token meesturen in Authorization header
  3. Token Refresh: Bij verlopen access token, /refresh endpoint aanroepen
  4. Logout: Sessie wordt ingetrokken en cookie verwijderd

Authorization Header

http
Authorization: Bearer <access_token>

Client Type Header

Voor bepaalde endpoints moet een x-client-type header worden meegestuurd:

http
x-client-type: adminportal

Of:

http
x-client-type: userdashboard

Rollen & Permissies

RolBeschrijvingRechten
adminAdministratorVolledige toegang tot alle endpoints
memberLid van hulpverleningszoneBeperkte toegang, kan branden aanmaken/bewerken

Endpoints

Health Check

GET /

Database connectiviteit check.

Response:

json
{
  "time": "2026-01-15T10:30:00.000Z"
}

GET /health

Simpele health check (geen database).

Response: 200 OK met body ok


Authentication Endpoints

Prefix: /api/v1

POST /login

Authenticeer een gebruiker en ontvang tokens.

Request Body:

json
{
  "email": "user@example.com",
  "wachtwoord": "minimaal8tekens",
  "client": "adminportal"
}
VeldTypeVerplichtBeschrijving
emailstringGeldig e-mailadres
wachtwoordstringMinimaal 8 karakters, hoofdletter en speciaal teken
clientenumadminportal of userdashboard

Response (200):

json
{
  "status": 200,
  "message": "Login succesvol!",
  "user": {
    "id": 1,
    "naam": "Doe",
    "voornaam": "John",
    "email": "user@example.com",
    "role": "admin",
    "Hulpverleningszones_id": 1,
    "hulpzone": "Zone Antwerpen"
  },
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Cookies: refreshToken_adminportal of refreshToken_userdashboard (httpOnly)

Errors:

  • 401 - Verkeerde credentials
  • 500 - Interne serverfout

POST /register-mail

Verstuur een registratie-uitnodiging per e-mail.

Authenticatie: ✅ JWT vereist
Rol: admin

Request Body:

json
{
  "email": "newuser@example.com",
  "role": "member"
}
VeldTypeVerplichtBeschrijving
emailstringE-mailadres van nieuwe gebruiker
roleenumadmin of member

Response (200):

json
{
  "status": 200,
  "message": "Registratie email succesvol verzonden!"
}

Errors:

  • 400 - Gebruiker met dit email bestaat al
  • 500 - Fout bij versturen email

POST /register

Voltooi de registratie met de ontvangen token.

Request Body:

json
{
  "token": "abc123resettoken",
  "voornaam": "John",
  "naam": "Doe",
  "email": "newuser@example.com",
  "defaultPassword": "tempwachtwoord123",
  "newPassword": "mijnnieuwwachtwoord"
}
VeldTypeVerplichtBeschrijving
tokenstringReset token uit e-mail
voornaamstringVoornaam
naamstringAchternaam
emailstringE-mailadres
defaultPasswordstringTijdelijk wachtwoord uit e-mail (min. 8 tekens)
newPasswordstringNieuw zelfgekozen wachtwoord (min. 8 tekens, moet anders zijn dan default)

Response (200):

json
{
  "status": 200,
  "message": "Registratie succesvol voltooid!",
  "user": { ... }
}

POST /validate-reset-token

Valideer of een reset token nog geldig is.

Request Body:

json
{
  "resetToken": "abc123resettoken"
}

Response (200):

json
{
  "status": 200,
  "message": "Token is valid",
  "email": "user@example.com",
  "role": "member"
}

Errors:

  • 400 - Token is invalid or expired

POST /refresh

Vernieuw het access token met de refresh token cookie.

Request Body:

json
{
  "client": "adminportal"
}

Response (200):

json
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Errors:

  • 401 - Geen refresh token / Ongeldig refresh token / Sessie ingetrokken

POST /logout

Log uit en trek de huidige sessie in.

Request Body:

json
{
  "client": "adminportal"
}

Response (200):

json
{
  "success": true
}

User Endpoints

Prefix: /api/v1

GET /users

Haal alle gebruikers op.

Authenticatie: ✅ JWT vereist
Rol: admin

Query Parameters:

ParameterTypeVerplichtBeschrijving
rolestringFilter op rol (admin, member)
zonenumberFilter op hulpverleningszone ID

Response (200):

json
{
  "success": true,
  "count": 5,
  "data": [
    {
      "id": 1,
      "naam": "Doe",
      "voornaam": "John",
      "email": "john@example.com",
      "role": "admin",
      "Hulpverleningszones_id": 1,
      "hulpzone": "Zone Antwerpen"
    }
  ]
}

PUT /users/:id

Werk een gebruiker bij.

Authenticatie: ✅ JWT vereist
Rol: admin, member (eigen profiel)

URL Parameters:

ParameterTypeBeschrijving
idnumberUser ID

Request Body (alle velden optioneel):

json
{
  "naam": "UpdatedName",
  "voornaam": "UpdatedFirstName",
  "email": "updated@example.com",
  "wachtwoord": "newpassword123",
  "Hulpverleningszones_id": 2,
  "role": "member"
}

Response (200):

json
{
  "status": 200,
  "message": "User updated successfully",
  "data": { ... }
}

Errors:

  • 403 - Forbidden (onvoldoende rechten)
  • 404 - User not found

DELETE /users/:id

Verwijder een gebruiker.

Authenticatie: ✅ JWT vereist
Rol: admin

URL Parameters:

ParameterTypeBeschrijving
idnumberUser ID

Response (200):

json
{
  "message": "User 5 deleted successfully",
  "id": 5
}

Errors:

  • 404 - User not found

Fire Endpoints

Prefix: /api/v1

GET /fires

Haal alle branden op als GeoJSON FeatureCollection.

Authenticatie: ❌ Optioneel (meer data beschikbaar met auth)
Header: x-client-type verplicht

Query Parameters: Filters kunnen toegepast worden via query parameters (afhankelijk van FireFilters type).

Response (200):

json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "centroid": {
        "type": "Point",
        "coordinates": [4.4025, 51.2194]
      },
      "properties": {
        "id": 1,
        "date": "2025-08-15T14:30:00.000Z",
        "date_nl": "15-8-2025",
        "province": "Antwerpen",
        "municipality": "Antwerpen",
        "hulpzone": "Zone Antwerpen",
        "status": "approved",
        "area_ha": 2.5,
        "type": "bos",
        "cause": "onbekend",
        "height_flame": 3.5,
        "dtm_mean_m": 15.2,
        "slope_mean": 5.3,
        "landcover": "Naaldbos",
        "risk_depic": "hoog"
      }
    }
  ]
}

Centroid Noise

Om privacy te beschermen wordt er willekeurige ruis toegevoegd aan de centroid coördinaten. De echte locatie is alleen beschikbaar via /fires/:id/geometry.


GET /fires/:id/geometry

Haal de volledige geometrie (polygoon) van een brand op.

Authenticatie: ✅ JWT vereist
Rol: admin, member

URL Parameters:

ParameterTypeBeschrijving
idnumberFire ID

Response (200):

json
{
  "type": "MultiPolygon",
  "coordinates": [
    [
      [
        [4.4025, 51.2194, 0],
        [4.4030, 51.2194, 0],
        [4.4030, 51.2200, 0],
        [4.4025, 51.2200, 0],
        [4.4025, 51.2194, 0]
      ]
    ]
  ]
}

POST /fires

Maak een nieuwe brand aan.

Authenticatie: ✅ JWT vereist
Rol: admin, member

Status

  • Admin: Brand krijgt automatisch status approved
  • Member: Brand krijgt automatisch status pending (moet goedgekeurd worden)

Request Body:

json
{
  "date": "2025-08-15T14:30:00.000Z",
  "reported_date": "2025-08-15T14:00:00.000Z",
  "ended_date": "2025-08-15T18:00:00.000Z",
  "alarmed_date": "2025-08-15T14:15:00.000Z",
  "province": "Antwerpen",
  "municipality": "Antwerpen",
  "hulpzone": "Zone Antwerpen",
  "users_id": 1,
  "geometry": {
    "type": "MultiPolygon",
    "coordinates": [[[[4.4025, 51.2194, 0], ...]]]
  },
  "area_ha": 2.5,
  "type": "bos",
  "function": "recreatie",
  "surface_gr": "oppervlakkig",
  "texture": "zand",
  "surface": "naaldboom",
  "tree_type": "grove den",
  "damaged_trees": 150,
  "litter_cm": 5,
  "affected_terrain": "bodem",
  "height_flame": 3.5,
  "damage_terrain": "licht",
  "damage_infrastructure": "geen",
  "cause": "onbekend",
  "bioclim": "gematigd",
  "anb_code": "ANB-2025-001",
  "reliability": "zeker",
  "observations": "Opmerking over de brand"
}

Verplichte velden:

VeldTypeBeschrijving
datestringDatum/tijd van de brand (ISO 8601)
provincestringProvincie
municipalitystringGemeente
hulpzonestringHulpverleningszone
users_idnumberID van de rapporterende gebruiker

Optionele velden:

VeldTypeBeschrijving
reported_datestringDatum wanneer brand gemeld werd
ended_datestringDatum wanneer brand geblust werd
alarmed_datestringDatum wanneer hulpdiensten gealarmeerd werden
geometryGeoJSON/stringPolygoon van het brandgebied
area_hanumberOppervlakte in hectare
typestringType gebied (open vlakte, bos)
functionstringFunctie (beschermend, economisch, recreatie)
surface_grstringType brand (oppervlakkig, kroon, ondergronds)
texturestringBodemtextuur
surfacestringOppervlaktetype
tree_typestringType bomen
damaged_treesnumberAantal beschadigde bomen
litter_cmnumberDikte strooisellaag in cm
affected_terrainstringAangetast terrein
height_flamenumberVlamhoogte in meters
damage_terrainstringSchade aan terrein
damage_infrastructurestringSchade aan infrastructuur
causestringOorzaak
bioclimstringBioklimaatzone
anb_codestringANB referentiecode
reliabilityenumverondersteld of zeker
observationsstringOpmerkingen

Response (201):

json
{
  "message": "Fire created"
}

Automatische Berekeningen

Bij het aanmaken van een brand worden automatisch berekend:

  • Centroid van de geometrie
  • DTM waarden (Digital Terrain Model: mean, min, max)
  • Hellingshoek (slope: mean, min, max)
  • Landcover classificatie
  • Oppervlakte in hectare
  • Risico-indicatie
  • Weerdata voor de branddatum

PUT /fires/:id

Werk een bestaande brand bij.

Authenticatie: ✅ JWT vereist
Rol: admin, member

URL Parameters:

ParameterTypeBeschrijving
idnumberFire ID

Request Body (alle velden uit create + extra velden):

json
{
  "status": "approved",
  "decline_reason": null,
  "dtm_mean_m": 15.2,
  "dtm_min_m": 10.5,
  "dtm_max_m": 20.1,
  "slope_mean": 5.3,
  "slope_min": 2.1,
  "slope_max": 8.7,
  "orientation": "NO",
  "landcover": "Naaldbos",
  "risk_depic": "hoog",
  "reported_by": "reporter@example.com"
}

Status waarden:

StatusBeschrijving
pendingWacht op goedkeuring
approvedGoedgekeurd
declinedAfgewezen (decline_reason verplicht)
deletedVerwijderd

Decline Reason

Wanneer status = declined, dan is decline_reason verplicht en mag niet leeg zijn.

Response (200):

json
{
  "message": "Fire updated"
}

DELETE /fires/:id

Verwijder een brand.

Authenticatie: ✅ JWT vereist
Rol: admin

URL Parameters:

ParameterTypeBeschrijving
idnumberFire ID

Response (200):

json
{
  "message": "Fire 5 deleted"
}

Weather Endpoints

Prefix: /api/v1/weather

De API haalt weerdata op van de Open-Meteo Archive API voor de locatie en datum van branden.

POST /fire/:id

Haal weerdata op van Open-Meteo en sla op voor een specifieke brand.

Authenticatie: ✅ JWT vereist
Rol: admin, member

URL Parameters:

ParameterTypeBeschrijving
idnumberFire ID

Response (200):

json
{
  "message": "Weather data fetched and stored successfully",
  "data": {
    "temperature_max": 28.5,
    "temperature_min": 18.2,
    "wind_speed_max": 15.3,
    "wind_direction": "SW",
    "precipitation_sum": 0,
    "precipitation_hours": 0,
    "humidity": 45,
    "weather_code": 1
  }
}

GET /fire/:id

Haal opgeslagen weerdata op voor een specifieke brand.

Authenticatie: ❌ Niet vereist

URL Parameters:

ParameterTypeBeschrijving
idnumberFire ID

Response (200):

json
{
  "id": 1,
  "Branden_id": 5,
  "date": "2025-08-15",
  "temperature_max": 28.5,
  "temperature_min": 18.2,
  "wind_speed_max": 15.3,
  "wind_direction": "SW",
  "precipitation_sum": 0,
  "precipitation_hours": 0,
  "humidity": 45,
  "weather_code": 1,
  "weather_description": "Mainly clear"
}

Weather Codes: De API converteert WMO weather codes naar beschrijvingen (bijv. 0 = "Clear sky", 1 = "Mainly clear", etc.)

Errors:

  • 400 - Invalid fire ID
  • 404 - No weather data found for this fire

Object Endpoints

Prefix: /api/v1/object

Statische referentiedata voor België.

GET /zones

Haal alle hulpverleningszones op.

Authenticatie: ❌ Niet vereist

Response (200):

json
[
  {
    "id": 1,
    "naam": "Hulpverleningszone Antwerpen"
  },
  {
    "id": 2,
    "naam": "Hulpverleningszone Oost-Vlaanderen"
  }
]

GET /provinces

Haal alle provincies op met hun gemeenten.

Authenticatie: ❌ Niet vereist

Response (200):

json
{
  "success": true,
  "count": 10,
  "data": [
    {
      "name": "Antwerpen",
      "municipalities": ["Antwerpen", "Mechelen", "Turnhout", ...]
    },
    {
      "name": "Oost-Vlaanderen",
      "municipalities": ["Gent", "Aalst", "Dendermonde", ...]
    }
  ]
}

Import Endpoints

Prefix: /api/v1/files

Endpoints voor het importeren van branddata via CSV bestanden. Het systeem ondersteunt twee types imports die automatisch worden gedetecteerd op basis van de kolomnamen in het CSV bestand.

BELANGRIJK: Kolomnamen zijn cruciaal!

De kolomnamen in je CSV bestand moeten exact overeenkomen met de verwachte namen (case-insensitive). Als de kolomnamen niet kloppen, zal de import mislukken of het verkeerde import type worden gebruikt!


Import Type Detectie

Het systeem detecteert automatisch welk type import uitgevoerd moet worden op basis van de aanwezige kolommen in de CSV. Beide types gebruiken dezelfde /import endpoint, maar worden intern anders verwerkt.

Detectie Logica

typescript
// Historical import wordt gedetecteerd als:
if (columns.includes("centroid") && 
    columns.includes("date") && 
    columns.includes("municipality") && 
    columns.includes("type") && 
    !columns.includes("geometry")) {
  return "historical";
}

// Real/Perimeter import wordt gedetecteerd als:
if (columns.includes("geometry") && columns.includes("province")) {
  return "real";
}
KolomHistoricalReal/Perimeter
centroid✅ Verplicht❌ Niet aanwezig
geometry❌ Niet aanwezig✅ Verplicht
province❌ Optioneel✅ Verplicht
date✅ Verplicht✅ Verplicht
municipality✅ Verplicht✅ Optioneel
type / Type✅ Verplicht❌ Optioneel

Import Type 1: Historical Import (Natuurbranddatabank)

Dit type wordt gebruikt voor het importeren van historische branddata uit de Natuurbranddatabank. Deze data bevat alleen punt-locaties (centroids) en minimale metadata.

Kenmerken

  • Bron: Natuurbranddatabank België
  • Geometrie: Alleen centroid (puntlocatie)
  • Data enrichment: Gemeente wordt automatisch opgezocht via centroid coördinaten indien niet opgegeven

Verplichte Kolommen Historical Import

KolomnaamTypeBeschrijving
centroidstringWKT POINT formaat, bijv. POINT(5.9658 50.5592)
datestringBranddatum (YYYY-MM-DD)
municipalitystringGemeente (wordt opgezocht als leeg)
TypestringType brand (bijv. "bos", "heide")

Optionele Kolommen Historical Import

KolomnaamTypeBeschrijving
centroid_sourcestringBron van de centroid (bijv. "google_maps")
AdresstringAdres informatie

Voorbeeld CSV (Historical)

csv
centroid,centroid_source,date,municipality,Adres,Type
"POINT(4.7234 50.8765)",google_maps,1985-07-23,Leuven,,bos
"POINT(3.9812 51.0234)",google_maps,1992-08-15,Gent,Forestlaan 12,heide
"POINT(5.1456 50.4321)",google_maps,2001-06-30,,,bos

Automatische Gemeente Detectie

Als de municipality kolom leeg is, wordt de gemeente automatisch bepaald via de centroid coördinaten met behulp van reverse geocoding.

Historical Import Flow

CSV Upload → Validate → Detect Type (historical)

For each row:
    1. Extract centroid coordinates
    2. If municipality empty → Reverse geocode from centroid
    3. Lookup province from municipality (belgiumData)
    4. Resolve hulpverleningszone
    5. Insert Locatie → Insert Branden → Insert Details

Post-import enrichment:
    - Fetch weather data
    - Fetch geo data (DTM, landcover)
    - Calculate slopes

Import Type 2: Real/Perimeter Import

Dit type wordt gebruikt voor het importeren van gedetailleerde branddata met polygoon-geometrie. Typisch afkomstig van drone/satelliet mapping na branden.

Kenmerken

  • Bron: Brandweer, ANB, drone mapping
  • Geometrie: Volledige polygoon (MultiPolygonZ)
  • Data: Uitgebreide metadata inclusief DTM, hellingshoek, landcover, etc.

Verplichte Kolommen Real Import

KolomnaamTypeBeschrijving
geometrystringWKT MULTIPOLYGON Z formaat
provincestringProvincie
datestringBranddatum (YYYY/MM/DD of YYYY-MM-DD)

Alle Ondersteunde Kolommen Real Import

KolomnaamTypeBeschrijving
geometrystringVerplicht - WKT MULTIPOLYGON Z
provincestringVerplicht - Provincie
datestringVerplicht - Branddatum
municipalitystringGemeente
zonestringHulpverleningszone
typestringType gebied (bos, heide, etc.)
area_hanumberOppervlakte in hectare
surface/ground/crownstringBrand type (surface_gr)
functionstringFunctie van het gebied
DTM_mean_mnumberGemiddelde hoogte (m)
DTM_min_mnumberMinimum hoogte (m)
DTM_max_mnumberMaximum hoogte (m)
Slope_meannumberGemiddelde hellingshoek
Slope_minnumberMinimum hellingshoek
Slope_maxnumberMaximum hellingshoek
OrientationstringOriëntatie (N, NE, E, etc.)
TexturestringBodemtextuur (sand, clay, etc.)
LandCoverstringLandcover classificatie
SurfacestringOppervlaktetype
Litter_cmnumberStrooisellaag dikte (cm)
Affected_tstringAangetast terrein
Height_flame_mnumberVlamhoogte (m)
Damage_terrainstringSchade aan terrein
Damage_infrastructurestringSchade aan infrastructuur
CausestringOorzaak
BioClimstringBioklimaatzone
Risk_DepicstringRisico classificatie
ANB_CodestringANB referentiecode

Voorbeeld CSV (Real/Perimeter)

csv
geometry,date,municipality,province,zone,type,area_ha,DTM_mean_m
"MULTIPOLYGON Z (((4.821 51.034 18.5,4.822 51.035 19.2,...)))",2025/04/12,Brasschaat,Antwerpen,Zone Antwerpen,bos,0.087,24.6
"MULTIPOLYGON Z (((5.234 50.876 32.1,5.235 50.877 33.8,...)))",2025/06/28,Maasmechelen,Limburg,Oost-Limburg,heide,0.42,52.3

Real Import Flow

CSV Upload → Validate → Detect Type (real)

For each row:
    1. Parse geometry (sanitize NaN/Infinity values)
    2. Check for duplicate (same geometry + location)
    3. Resolve hulpverleningszone
    4. Insert Locatie → Insert Branden → Insert Details

Post-import enrichment:
    - Fetch weather data
    - Fetch geo data (if not provided)
    - Calculate slopes (if not provided)
    - Calculate centroids

Vergelijking Import Types

AspectHistoricalReal/Perimeter
GeometrieAlleen centroid (punt)Volledige polygoon (niet verplicht)
Automatische verrijkingUitgebreidAlleen als data ontbreekt

API Endpoints

POST /import/validate

Valideer een CSV bestand voor import. De validatie controleert:

  • CSV structuur en parsing
  • Verplichte kolommen aanwezig
  • Data types correct
  • Import type detectie

Authenticatie: ✅ JWT vereist
Rol: admin

Content-Type: multipart/form-data

Request:

file: <CSV bestand>

Response (200):

json
{
  "valid": true,
  "rowCount": 150,
  "importType": "historical",
  "errors": [],
  "warnings": [
    "Row 5: municipality empty, will be geocoded from centroid"
  ],
  "rows": [
    {
      "centroid": "POINT(5.9658 50.5592)",
      "date": "1923-08-16",
      "municipality": "Jalhay",
      "Type": "bos"
    }
  ]
}

Errors:

  • 400 - No file uploaded
  • 400 - Invalid CSV format
  • 400 - Unknown CSV format (kolommen niet herkend)

POST /import

Importeer gevalideerde branddata. Het import type wordt automatisch gedetecteerd.

Authenticatie: ✅ JWT vereist
Rol: admin

Request Body:

json
{
  "rows": [
    {
      "centroid": "POINT(5.9658 50.5592)",
      "date": "1923-08-16",
      "municipality": "Jalhay",
      "Type": "bos"
    }
  ]
}

Of voor real import:

json
{
  "rows": [
    {
      "geometry": "MULTIPOLYGON Z (((...)))",
      "date": "2025-05-19",
      "province": "Limburg",
      "municipality": "HamontAche",
      "zone": "Noord-Limburg",
      "area_ha": 0.013
    }
  ]
}

Response (200):

json
{
  "imported": 150,
  "ids": [1, 2, 3, ...]
}

Errors:

  • 400 - No rows provided
  • 400 - Validation failed (met details per rij)
  • 400 - Unsupported import type

Post-Import Data Enrichment

Na een succesvolle import worden automatisch de volgende data-verrijkingen uitgevoerd:

  1. Weather Data - Weerdata ophalen van Open-Meteo voor de branddatum
  2. Geo Data - DTM waarden, landcover classificatie, oppervlakte berekening
  3. Slope Data - Hellingshoek berekeningen

INFO

Als de import zelf slaagt maar de data-verrijking faalt, worden de branden toch opgeslagen. De verrijking is supplementair en kan later opnieuw worden uitgevoerd.


Security Endpoints

Prefix: /api/v1/security

Interne security audit endpoints.

GET /full-report

Genereer een volledige security audit rapport.

Authenticatie: ❌ Niet vereist (intern gebruik)

Query Parameters:

ParameterTypeBeschrijving
savebooleanSla rapport op naar bestand

Response (200):

json
{
  "timestamp": "2026-01-15T10:30:00.000Z",
  "audit": {
    "passed": true,
    "summary": { ... },
    "highCvssVulnerabilities": []
  },
  "credentials": {
    "passed": true,
    "findings": []
  },
  "overallStatus": "PASSED"
}

Data Models

User Model

typescript
interface User {
  id: number;
  naam: string;           // Achternaam
  voornaam: string;       // Voornaam
  email: string;
  role: "admin" | "member";
  Hulpverleningszones_id: number;
  hulpzone?: string;      // Naam van de zone (joined)
}

Fire Model (Properties in GeoJSON)

typescript
interface FireProperties {
  id: number;
  date: string;                    // ISO datetime
  date_nl: string;                 // Nederlandse datum formattering
  reported_date?: string;
  ended_date?: string;
  alarmed_date?: string;
  
  // Locatie
  province: string;
  municipality: string;
  hulpzone: string;
  
  // Status
  status: "pending" | "approved" | "declined" | "deleted";
  decline_reason?: string;
  
  // Terrein info
  area_ha?: number;                // Oppervlakte in hectare
  type?: string;                   // bos, heide, etc.
  function?: string;               // beschermend, economisch, recreatie
  surface_gr?: string;             // oppervlakkig, kroon, ondergronds
  
  // DTM (Digital Terrain Model)
  dtm_mean_m?: number;
  dtm_min_m?: number;
  dtm_max_m?: number;
  
  // Helling
  slope_mean?: number;
  slope_min?: number;
  slope_max?: number;
  orientation?: string;            // N, NE, E, SE, S, SW, W, NW
  
  // Vegetatie
  landcover?: string;
  surface?: string;
  texture?: string;
  tree_type?: string;
  damaged_trees?: number;
  litter_cm?: number;
  
  // Brand details
  height_flame?: number;           // Vlamhoogte in meters
  affected_terrain?: string;
  damage_terrain?: string;
  damage_infrastructure?: string;
  
  // Meta
  cause?: string;
  bioclim?: string;
  risk_depic?: string;             // Risico-indicatie
  anb_code?: string;
  reliability?: "verondersteld" | "zeker";
  observations?: string;
}

Weather Model

typescript
interface FireWeather {
  id: number;
  Branden_id: number;
  date: string;
  temperature_max: number;         // °C
  temperature_min: number;         // °C
  wind_speed_max: number;          // km/h
  wind_direction: string;          // N, NE, E, SE, S, SW, W, NW
  precipitation_sum: number;       // mm
  precipitation_hours: number;
  humidity: number;                // %
  weather_code: number;            // WMO code
  weather_description?: string;    // Beschrijving van WMO code
}

Error Handling

Alle errors volgen een consistent formaat:

Standaard Error Response

json
{
  "error": "Error message describing what went wrong",
  "status": 400
}

Validatie Error Response

json
{
  "error": "Invalid request data",
  "issues": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["email"],
      "message": "Required"
    }
  ]
}

HTTP Status Codes

CodeBetekenisWanneer
200OKSuccesvolle request
201CreatedResource aangemaakt
400Bad RequestValidatie gefaald, ongeldige data
401UnauthorizedNiet ingelogd, token verlopen/ongeldig
403ForbiddenOnvoldoende rechten
404Not FoundResource niet gevonden
500Internal Server ErrorServer error

Specifieke Auth Errors

ErrorBetekenis
token_expiredAccess token is verlopen, gebruik /refresh
session_not_foundSessie bestaat niet meer
session_expiredSessie is verlopen
session revokedSessie is ingetrokken (bijv. na wachtwoord wijziging)

Database Schema

De backend gebruikt PostgreSQL met PostGIS voor geografische queries.

Tabellen Overzicht

TabelBeschrijving
UsersGebruikers met rollen
SessionsActieve login sessies
RolesRol definities
HulpverleningszonesBelgische hulpverleningszones
LocatiesProvincie/gemeente combinaties
BrandenHoofdtabel voor branden
DetailsGedetailleerde brand info + geometrie
Fire_WeatherWeerdata per brand
FotosFoto's van branden

Geometrie

  • Type: GEOMETRY(MultiPolygonZ, 4326)
  • Coordinaten Systeem: WGS 84 (EPSG:4326)
  • PostGIS functies worden gebruikt voor:
    • Centroid berekening (ST_Centroid)
    • Oppervlakte berekening (ST_Area)
    • Coördinaat transformaties

Environment Variables

Belangrijke configuratie variabelen voor de backend:

bash
# Server
PORT=3000
HTTPS_PORT=3443
NODE_ENV=development

# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=fms
DB_USER=postgres
DB_PASSWORD=secret

# JWT
JWT_SECRET=your-secret-key
ACCESS_TOKEN_EXPIRES_MINUTES=15
REFRESH_TOKEN_EXPIRES_DAYS=30

# CORS
ALLOWED_ORIGINS=http://localhost:3001,http://localhost:3002

# Frontend URL (voor emails)
FRONTEND_URL=http://localhost:3001

# Privacy
CENTROID_NOISE=500

# Features
RUN_SECURITY_AUDIT_ON_STARTUP=false

Middleware

De API gebruikt verschillende middleware functies:

jwtAuth

Verifieert JWT tokens en haalt sessie informatie op.

optionalJwtAuth

Zoals jwtAuth maar faalt niet als er geen token is (voor mixed-access endpoints).

permittedRoles(...roles)

Controleert of de ingelogde gebruiker een van de toegestane rollen heeft.

validate(schema)

Valideert request body tegen een Zod schema.

validateParams(schema)

Valideert URL parameters tegen een Zod schema.

errorHandler

Vangt alle errors op en formatteert ze consistent.


Best Practices

Voor Frontend Developers

  1. Altijd x-client-type header meesturen bij fire endpoints
  2. Access tokens cachen en alleen refreshen bij 401 token_expired
  3. Error handling implementeren voor alle mogelijke error codes
  4. GeoJSON features gebruiken voor kaartweergave

Voor API Consumers

  1. Rate limiting: Houd rekening met server load
  2. Batch imports: Gebruik /import voor grote datasets
  3. Weather data: Wordt automatisch opgehaald bij fire creation
  4. Centroid noise: Exacte locatie alleen beschikbaar voor authenticated users

Zie Ook

Fire Management System Documentation