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
| Component | Technologie |
|---|---|
| Runtime | Node.js |
| Framework | Express 4.x |
| Taal | TypeScript |
| Database | PostgreSQL + PostGIS |
| Authenticatie | JWT (Access + Refresh tokens) |
| Validatie | Zod |
| Documentatie | Swagger/OpenAPI 3.0 |
| DI Container | tsyringe |
Base URLs
| Omgeving | HTTP | HTTPS |
|---|---|---|
| Development | http://localhost:3000 | https://localhost:3443 |
| Production | Configureerbaar via .env | Configureerbaar 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/docsAuthenticatie
De API gebruikt een JWT-gebaseerd authenticatiesysteem met access en refresh tokens.
Hoe het werkt
- Login: Gebruiker logt in met email/wachtwoord → ontvangt access token + refresh token (in httpOnly cookie)
- API Requests: Access token meesturen in
Authorizationheader - Token Refresh: Bij verlopen access token,
/refreshendpoint aanroepen - Logout: Sessie wordt ingetrokken en cookie verwijderd
Authorization Header
Authorization: Bearer <access_token>Client Type Header
Voor bepaalde endpoints moet een x-client-type header worden meegestuurd:
x-client-type: adminportalOf:
x-client-type: userdashboardRollen & Permissies
| Rol | Beschrijving | Rechten |
|---|---|---|
admin | Administrator | Volledige toegang tot alle endpoints |
member | Lid van hulpverleningszone | Beperkte toegang, kan branden aanmaken/bewerken |
Endpoints
Health Check
GET /
Database connectiviteit check.
Response:
{
"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:
{
"email": "user@example.com",
"wachtwoord": "minimaal8tekens",
"client": "adminportal"
}| Veld | Type | Verplicht | Beschrijving |
|---|---|---|---|
email | string | ✅ | Geldig e-mailadres |
wachtwoord | string | ✅ | Minimaal 8 karakters, hoofdletter en speciaal teken |
client | enum | ✅ | adminportal of userdashboard |
Response (200):
{
"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 credentials500- Interne serverfout
POST /register-mail
Verstuur een registratie-uitnodiging per e-mail.
Authenticatie: ✅ JWT vereist
Rol: admin
Request Body:
{
"email": "newuser@example.com",
"role": "member"
}| Veld | Type | Verplicht | Beschrijving |
|---|---|---|---|
email | string | ✅ | E-mailadres van nieuwe gebruiker |
role | enum | ✅ | admin of member |
Response (200):
{
"status": 200,
"message": "Registratie email succesvol verzonden!"
}Errors:
400- Gebruiker met dit email bestaat al500- Fout bij versturen email
POST /register
Voltooi de registratie met de ontvangen token.
Request Body:
{
"token": "abc123resettoken",
"voornaam": "John",
"naam": "Doe",
"email": "newuser@example.com",
"defaultPassword": "tempwachtwoord123",
"newPassword": "mijnnieuwwachtwoord"
}| Veld | Type | Verplicht | Beschrijving |
|---|---|---|---|
token | string | ✅ | Reset token uit e-mail |
voornaam | string | ✅ | Voornaam |
naam | string | ✅ | Achternaam |
email | string | ✅ | E-mailadres |
defaultPassword | string | ✅ | Tijdelijk wachtwoord uit e-mail (min. 8 tekens) |
newPassword | string | ✅ | Nieuw zelfgekozen wachtwoord (min. 8 tekens, moet anders zijn dan default) |
Response (200):
{
"status": 200,
"message": "Registratie succesvol voltooid!",
"user": { ... }
}POST /validate-reset-token
Valideer of een reset token nog geldig is.
Request Body:
{
"resetToken": "abc123resettoken"
}Response (200):
{
"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:
{
"client": "adminportal"
}Response (200):
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Errors:
401- Geen refresh token / Ongeldig refresh token / Sessie ingetrokken
POST /logout
Log uit en trek de huidige sessie in.
Request Body:
{
"client": "adminportal"
}Response (200):
{
"success": true
}User Endpoints
Prefix: /api/v1
GET /users
Haal alle gebruikers op.
Authenticatie: ✅ JWT vereist
Rol: admin
Query Parameters:
| Parameter | Type | Verplicht | Beschrijving |
|---|---|---|---|
role | string | ❌ | Filter op rol (admin, member) |
zone | number | ❌ | Filter op hulpverleningszone ID |
Response (200):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | User ID |
Request Body (alle velden optioneel):
{
"naam": "UpdatedName",
"voornaam": "UpdatedFirstName",
"email": "updated@example.com",
"wachtwoord": "newpassword123",
"Hulpverleningszones_id": 2,
"role": "member"
}Response (200):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | User ID |
Response (200):
{
"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):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | Fire ID |
Response (200):
{
"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:
{
"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:
| Veld | Type | Beschrijving |
|---|---|---|
date | string | Datum/tijd van de brand (ISO 8601) |
province | string | Provincie |
municipality | string | Gemeente |
hulpzone | string | Hulpverleningszone |
users_id | number | ID van de rapporterende gebruiker |
Optionele velden:
| Veld | Type | Beschrijving |
|---|---|---|
reported_date | string | Datum wanneer brand gemeld werd |
ended_date | string | Datum wanneer brand geblust werd |
alarmed_date | string | Datum wanneer hulpdiensten gealarmeerd werden |
geometry | GeoJSON/string | Polygoon van het brandgebied |
area_ha | number | Oppervlakte in hectare |
type | string | Type gebied (open vlakte, bos) |
function | string | Functie (beschermend, economisch, recreatie) |
surface_gr | string | Type brand (oppervlakkig, kroon, ondergronds) |
texture | string | Bodemtextuur |
surface | string | Oppervlaktetype |
tree_type | string | Type bomen |
damaged_trees | number | Aantal beschadigde bomen |
litter_cm | number | Dikte strooisellaag in cm |
affected_terrain | string | Aangetast terrein |
height_flame | number | Vlamhoogte in meters |
damage_terrain | string | Schade aan terrein |
damage_infrastructure | string | Schade aan infrastructuur |
cause | string | Oorzaak |
bioclim | string | Bioklimaatzone |
anb_code | string | ANB referentiecode |
reliability | enum | verondersteld of zeker |
observations | string | Opmerkingen |
Response (201):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | Fire ID |
Request Body (alle velden uit create + extra velden):
{
"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:
| Status | Beschrijving |
|---|---|
pending | Wacht op goedkeuring |
approved | Goedgekeurd |
declined | Afgewezen (decline_reason verplicht) |
deleted | Verwijderd |
Decline Reason
Wanneer status = declined, dan is decline_reason verplicht en mag niet leeg zijn.
Response (200):
{
"message": "Fire updated"
}DELETE /fires/:id
Verwijder een brand.
Authenticatie: ✅ JWT vereist
Rol: admin
URL Parameters:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | Fire ID |
Response (200):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | Fire ID |
Response (200):
{
"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:
| Parameter | Type | Beschrijving |
|---|---|---|
id | number | Fire ID |
Response (200):
{
"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 ID404- 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):
[
{
"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):
{
"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
// 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";
}| Kolom | Historical | Real/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
| Kolomnaam | Type | Beschrijving |
|---|---|---|
centroid | string | WKT POINT formaat, bijv. POINT(5.9658 50.5592) |
date | string | Branddatum (YYYY-MM-DD) |
municipality | string | Gemeente (wordt opgezocht als leeg) |
Type | string | Type brand (bijv. "bos", "heide") |
Optionele Kolommen Historical Import
| Kolomnaam | Type | Beschrijving |
|---|---|---|
centroid_source | string | Bron van de centroid (bijv. "google_maps") |
Adres | string | Adres informatie |
Voorbeeld CSV (Historical)
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,,,bosAutomatische 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 slopesImport 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
| Kolomnaam | Type | Beschrijving |
|---|---|---|
geometry | string | WKT MULTIPOLYGON Z formaat |
province | string | Provincie |
date | string | Branddatum (YYYY/MM/DD of YYYY-MM-DD) |
Alle Ondersteunde Kolommen Real Import
| Kolomnaam | Type | Beschrijving |
|---|---|---|
geometry | string | Verplicht - WKT MULTIPOLYGON Z |
province | string | Verplicht - Provincie |
date | string | Verplicht - Branddatum |
municipality | string | Gemeente |
zone | string | Hulpverleningszone |
type | string | Type gebied (bos, heide, etc.) |
area_ha | number | Oppervlakte in hectare |
surface/ground/crown | string | Brand type (surface_gr) |
function | string | Functie van het gebied |
DTM_mean_m | number | Gemiddelde hoogte (m) |
DTM_min_m | number | Minimum hoogte (m) |
DTM_max_m | number | Maximum hoogte (m) |
Slope_mean | number | Gemiddelde hellingshoek |
Slope_min | number | Minimum hellingshoek |
Slope_max | number | Maximum hellingshoek |
Orientation | string | Oriëntatie (N, NE, E, etc.) |
Texture | string | Bodemtextuur (sand, clay, etc.) |
LandCover | string | Landcover classificatie |
Surface | string | Oppervlaktetype |
Litter_cm | number | Strooisellaag dikte (cm) |
Affected_t | string | Aangetast terrein |
Height_flame_m | number | Vlamhoogte (m) |
Damage_terrain | string | Schade aan terrein |
Damage_infrastructure | string | Schade aan infrastructuur |
Cause | string | Oorzaak |
BioClim | string | Bioklimaatzone |
Risk_Depic | string | Risico classificatie |
ANB_Code | string | ANB referentiecode |
Voorbeeld CSV (Real/Perimeter)
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.3Real 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 centroidsVergelijking Import Types
| Aspect | Historical | Real/Perimeter |
|---|---|---|
| Geometrie | Alleen centroid (punt) | Volledige polygoon (niet verplicht) |
| Automatische verrijking | Uitgebreid | Alleen 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):
{
"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 uploaded400- Invalid CSV format400- Unknown CSV format (kolommen niet herkend)
POST /import
Importeer gevalideerde branddata. Het import type wordt automatisch gedetecteerd.
Authenticatie: ✅ JWT vereist
Rol: admin
Request Body:
{
"rows": [
{
"centroid": "POINT(5.9658 50.5592)",
"date": "1923-08-16",
"municipality": "Jalhay",
"Type": "bos"
}
]
}Of voor real import:
{
"rows": [
{
"geometry": "MULTIPOLYGON Z (((...)))",
"date": "2025-05-19",
"province": "Limburg",
"municipality": "HamontAche",
"zone": "Noord-Limburg",
"area_ha": 0.013
}
]
}Response (200):
{
"imported": 150,
"ids": [1, 2, 3, ...]
}Errors:
400- No rows provided400- 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:
- Weather Data - Weerdata ophalen van Open-Meteo voor de branddatum
- Geo Data - DTM waarden, landcover classificatie, oppervlakte berekening
- 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:
| Parameter | Type | Beschrijving |
|---|---|---|
save | boolean | Sla rapport op naar bestand |
Response (200):
{
"timestamp": "2026-01-15T10:30:00.000Z",
"audit": {
"passed": true,
"summary": { ... },
"highCvssVulnerabilities": []
},
"credentials": {
"passed": true,
"findings": []
},
"overallStatus": "PASSED"
}Data Models
User Model
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)
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
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
{
"error": "Error message describing what went wrong",
"status": 400
}Validatie Error Response
{
"error": "Invalid request data",
"issues": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["email"],
"message": "Required"
}
]
}HTTP Status Codes
| Code | Betekenis | Wanneer |
|---|---|---|
200 | OK | Succesvolle request |
201 | Created | Resource aangemaakt |
400 | Bad Request | Validatie gefaald, ongeldige data |
401 | Unauthorized | Niet ingelogd, token verlopen/ongeldig |
403 | Forbidden | Onvoldoende rechten |
404 | Not Found | Resource niet gevonden |
500 | Internal Server Error | Server error |
Specifieke Auth Errors
| Error | Betekenis |
|---|---|
token_expired | Access token is verlopen, gebruik /refresh |
session_not_found | Sessie bestaat niet meer |
session_expired | Sessie is verlopen |
session revoked | Sessie is ingetrokken (bijv. na wachtwoord wijziging) |
Database Schema
De backend gebruikt PostgreSQL met PostGIS voor geografische queries.
Tabellen Overzicht
| Tabel | Beschrijving |
|---|---|
Users | Gebruikers met rollen |
Sessions | Actieve login sessies |
Roles | Rol definities |
Hulpverleningszones | Belgische hulpverleningszones |
Locaties | Provincie/gemeente combinaties |
Branden | Hoofdtabel voor branden |
Details | Gedetailleerde brand info + geometrie |
Fire_Weather | Weerdata per brand |
Fotos | Foto'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
- Centroid berekening (
Environment Variables
Belangrijke configuratie variabelen voor de backend:
# 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=falseMiddleware
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
- Altijd
x-client-typeheader meesturen bij fire endpoints - Access tokens cachen en alleen refreshen bij
401 token_expired - Error handling implementeren voor alle mogelijke error codes
- GeoJSON features gebruiken voor kaartweergave
Voor API Consumers
- Rate limiting: Houd rekening met server load
- Batch imports: Gebruik
/importvoor grote datasets - Weather data: Wordt automatisch opgehaald bij fire creation
- Centroid noise: Exacte locatie alleen beschikbaar voor authenticated users
Zie Ook
- Backend Setup - Installatie instructies
- Deployment Guide - Production deployment
- Database Schema - Volledige database documentatie