17 KiB
Wegwichtel REST-API
Diese Datei dokumentiert die vollständige HTTP-Schnittstelle des Wegwichtel-Servers. Die API-Basis lautet /api. Bild- und Audiodateien werden ebenfalls ausschließlich über API-Endpunkte ausgeliefert; die Clientanwendung verwendet dafür keine direkten Speicherpfade.
1. Grundlagen
Basisadressen
Lokaler Standard:
http://127.0.0.1:47145
API-Basis:
http://127.0.0.1:47145/api
Bei vorgeschaltetem Nginx bleibt der Pfad erhalten, zum Beispiel:
https://wegwichtel.example.org/api
Formate
- JSON-Lesezugriffe liefern
application/json. - Bild- und Audio-GETs liefern den jeweiligen binären Medientyp.
- Schreibzugriffe ohne Datei akzeptieren JSON, URL-encoded Formulare oder
multipart/form-data. - Datei-Uploads verwenden
multipart/form-data. - Pro Medienrequest wird genau eine Datei verarbeitet.
- Zeitstempel werden als SQLite- beziehungsweise ISO-8601-Text ausgegeben.
- Die derzeitige API besitzt noch keine Authentifizierung. Schreibzugriffe dürfen nicht ungeschützt öffentlich erreichbar sein.
Uploadgrenzen und unterstützte Dateitypen
Das Dateilimit pro Upload wird mit MAX_UPLOAD_MB festgelegt und beträgt standardmäßig 50 MB.
| Ressource | Uploadfeld | Dateitypen |
|---|---|---|
| GPX | gpx |
.gpx, application/gpx+xml, XML |
| Bild | picture |
JPEG, PNG, WebP |
| Audio | audio |
MP3, MP4/M4A, AAC, Ogg, WAV, WebM |
Fehlerformat
{
"error": "HttpError",
"message": "Strecke nicht gefunden."
}
Typische Statuscodes:
| Status | Bedeutung |
|---|---|
200 |
Anfrage erfolgreich |
201 |
Ressource wurde angelegt |
400 |
Parameter oder Datei fehlt beziehungsweise ist ungültig |
404 |
Route, POI oder Medienressource wurde nicht gefunden |
409 |
Ressource existiert bereits oder widerspricht dem aktuellen Zustand |
413 |
Datei überschreitet MAX_UPLOAD_MB |
415 |
Dateityp wird nicht unterstützt |
500 |
Interner Serverfehler |
IDs und Medienadressierung
:idbezeichnet bei/routes/:id/...die Route.:pictureIdbezeichnet ein einzelnes Bild auspoi_images.poiIdbezeichnet einen POI innerhalb einer Route.- Ein POI kann mehrere Bilder, aber höchstens eine Audiodatei besitzen.
- Bilder erhalten deshalb eine eigene Pfad-ID.
- Audio verwendet keinen zusätzlichen Pfadabschnitt; der POI wird über
poiIdangegeben.
Beispiele verwenden Route 1, POI 7 und Bild 15.
2. Datenmodelle
Route
{
"id": 1,
"slug": "schulwald-runde",
"name": "Schulwald-Runde",
"description": "Naturkundlicher Rundweg",
"schoolName": "Beispielschule",
"status": "active",
"start": { "lat": 52.5208, "lon": 13.407 },
"center": { "lat": 52.521, "lon": 13.408 },
"bounds": {
"minLat": 52.5208,
"minLon": 13.407,
"maxLat": 52.5212,
"maxLon": 13.409
},
"distanceM": 842.6,
"elevationGainM": 14.2,
"pointCount": 87,
"gpxUrl": "/media/routes/1/route.gpx",
"createdAt": "2026-06-16 12:00:00",
"updatedAt": "2026-06-16 12:00:00",
"deletedAt": null
}
POI
{
"id": 7,
"routeId": 1,
"title": "Die alte Eiche",
"description": "Hier wird das Alter der Eiche erklärt.",
"lat": 52.5208,
"lon": 13.407,
"triggerRadiusM": 60,
"sequence": 2,
"audioUrl": "/api/routes/1/audio?poiId=7",
"images": [
{
"id": 15,
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pictures/15"
}
]
}
Bildmetadaten
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pictures/15",
"createdAt": "2026-06-16 12:30:00"
}
Audiometadaten
{
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"url": "/api/routes/1/audio?poiId=7",
"updatedAt": "2026-06-16 12:35:00"
}
3. Endpunktübersicht
| Methode | Pfad | Zweck |
|---|---|---|
GET |
/api/health |
Server- und SQLite-Zustand |
GET |
/api/routes |
Routen auflisten |
GET |
/api/routes/:id |
Route mit Punkten und POIs lesen |
POST |
/api/routes |
Route anlegen |
PUT |
/api/routes/:id |
Route aktualisieren |
POST |
/api/routes/:id/append |
GPX-Punkte anhängen |
DELETE |
/api/routes/:id |
Route weich löschen |
POST |
/api/routes/:id/restore |
Route wiederherstellen |
GET |
/api/routes/:id/pois |
POIs einer Route auflisten |
GET |
/api/pois/:id |
einzelnen POI lesen |
POST |
/api/routes/:id/pois |
POI anlegen |
PUT |
/api/pois/:id |
POI aktualisieren |
GET |
/api/routes/:id/pictures |
Bildmetadaten auflisten |
GET |
/api/routes/:id/pictures/:pictureId |
Bilddatei ausliefern |
POST |
/api/routes/:id/pictures |
Bild anlegen |
PUT |
/api/routes/:id/pictures/:pictureId |
Bilddatei oder Metadaten aktualisieren |
DELETE |
/api/routes/:id/pictures/:pictureId |
Bild löschen |
GET |
/api/routes/:id/audio |
Audio auflisten oder für einen POI ausliefern |
POST |
/api/routes/:id/audio |
Audio für einen POI anlegen |
PUT |
/api/routes/:id/audio |
Audio eines POIs ersetzen |
DELETE |
/api/routes/:id/audio |
Audio eines POIs löschen |
4. Systemzustand
GET /api/health
Parameter: keine.
curl http://127.0.0.1:47145/api/health
Beispielantwort:
{
"ok": true,
"service": "wegwichtel",
"socket": "127.0.0.1:47145",
"sqliteVersion": "3.46.1",
"timestamp": "2026-06-16T12:00:00.000Z"
}
5. Routen
GET /api/routes
Listet aktive Routen auf. Werden lat und lon gemeinsam angegeben, berechnet der Server die Entfernung zum Routenstart, filtert mit radiusKm und sortiert nach Entfernung.
Query-Parameter:
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
lat |
Dezimalzahl | nein | – | Breitengrad des Ausgangspunkts |
lon |
Dezimalzahl | nein | – | Längengrad des Ausgangspunkts |
radiusKm |
Dezimalzahl | nein | DEFAULT_ROUTE_RADIUS_KM |
maximaler Abstand zum Routenstart |
includeDeleted |
true/false |
nein | false |
gelöschte Routen einschließen |
Alle aktiven Routen:
curl http://127.0.0.1:47145/api/routes
Mit allen Query-Parametern:
curl --get http://127.0.0.1:47145/api/routes \
--data-urlencode 'lat=52.5208' \
--data-urlencode 'lon=13.4070' \
--data-urlencode 'radiusKm=12.5' \
--data-urlencode 'includeDeleted=true'
GET /api/routes/:id
Pfadparameter:
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
id |
positive Ganzzahl | ja | Route |
Query-Parameter:
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
includeDeleted |
true/false |
nein | false |
auch eine weich gelöschte Route lesen |
curl http://127.0.0.1:47145/api/routes/1
Die Antwort enthält points, pois, images[].url und audioUrl. Die Medien-URLs zeigen auf die API-Endpunkte.
POST /api/routes
Content-Type: multipart/form-data.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
gpx |
Datei | ja | GPX-Datei |
name |
Text | nein, wenn GPX einen Namen enthält | Anzeigename |
slug |
Text | nein | gewünschter URL-tauglicher Bezeichner |
description |
Text | nein | Beschreibung |
schoolName |
Text | nein | Schule oder Projekt |
curl -X POST http://127.0.0.1:47145/api/routes \
-F 'name=Schulwald-Runde' \
-F 'slug=schulwald-runde' \
-F 'description=Naturkundlicher Rundweg' \
-F 'schoolName=Beispielschule' \
-F 'gpx=@route.gpx;type=application/gpx+xml'
PUT /api/routes/:id
Aktualisiert Routendaten. Eine neue GPX-Datei ersetzt die gespeicherten Trackpunkte.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
name |
Text | nein | neuer Name |
description |
Text | nein | neue Beschreibung |
schoolName |
Text | nein | neue Schule |
gpx |
Datei | nein | vollständige Ersatzstrecke |
curl -X PUT http://127.0.0.1:47145/api/routes/1 \
-F 'name=Schulwald-Runde 2026' \
-F 'description=Überarbeitete Route' \
-F 'schoolName=Beispielschule' \
-F 'gpx=@route-neu.gpx;type=application/gpx+xml'
POST /api/routes/:id/append
Hängt GPX-Punkte an eine Route an.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
gpx |
Datei | ja | GPX-Datei mit zusätzlichen Punkten |
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
-F 'gpx=@erweiterung.gpx;type=application/gpx+xml'
DELETE /api/routes/:id
Markiert die Route als gelöscht und verschiebt GPX, Bilder und Audio in den Papierkorb.
curl -X DELETE http://127.0.0.1:47145/api/routes/1
POST /api/routes/:id/restore
Stellt eine weich gelöschte Route einschließlich ihrer Dateien wieder her.
curl -X POST http://127.0.0.1:47145/api/routes/1/restore
6. POIs
GET /api/routes/:id/pois
curl http://127.0.0.1:47145/api/routes/1/pois
GET /api/pois/:id
curl http://127.0.0.1:47145/api/pois/7
POST /api/routes/:id/pois
Akzeptiert JSON, URL-encoded Formulare oder Multipart ohne Mediendateien.
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
title |
Text | nein | Unbenannter POI |
Titel |
description |
Text | nein | leer | Beschreibung |
lat |
Dezimalzahl | ja | – | Breitengrad |
lon |
Dezimalzahl | ja | – | Längengrad |
triggerRadiusM |
Dezimalzahl | nein | DEFAULT_POI_TRIGGER_METERS |
Aktivierungsradius in Metern |
sequence |
Ganzzahl ≥ 0 | nein | 0 |
Reihenfolge |
curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
-H 'Content-Type: application/json' \
-d '{
"title": "Die alte Eiche",
"description": "Informationen zur Baumart",
"lat": 52.5208,
"lon": 13.4070,
"triggerRadiusM": 60,
"sequence": 2
}'
PUT /api/pois/:id
Alle Felder sind optional; nicht angegebene Werte bleiben erhalten.
curl -X PUT http://127.0.0.1:47145/api/pois/7 \
-H 'Content-Type: application/json' \
-d '{
"title": "Die sehr alte Eiche",
"description": "Aktualisierte Beschreibung",
"lat": 52.5209,
"lon": 13.4071,
"triggerRadiusM": 45,
"sequence": 1
}'
7. Bilder
Ein POI kann mehrere Bilder besitzen. Die Collection wird über die Route adressiert; jedes Bild hat zusätzlich eine pictureId.
GET /api/routes/:id/pictures
Liefert Bildmetadaten als JSON.
Query-Parameter:
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
poiId |
positive Ganzzahl | nein | auf Bilder eines POIs einschränken |
Alle Bilder der Route:
curl http://127.0.0.1:47145/api/routes/1/pictures
Nur Bilder von POI 7:
curl --get http://127.0.0.1:47145/api/routes/1/pictures \
--data-urlencode 'poiId=7'
Beispielantwort:
{
"pictures": [
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pictures/15",
"createdAt": "2026-06-16 12:30:00"
}
]
}
GET /api/routes/:id/pictures/:pictureId
Liefert die Bilddatei binär mit dem passenden Content-Type, beispielsweise image/jpeg, image/png oder image/webp.
curl http://127.0.0.1:47145/api/routes/1/pictures/15 \
--output bild-15.jpg
Bildmetadaten werden über GET /api/routes/:id/pictures gelesen.
POST /api/routes/:id/pictures
Content-Type: multipart/form-data.
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
picture |
Datei | ja | – | Bilddatei |
poiId |
positive Ganzzahl | ja | – | zugehöriger POI |
caption |
Text | nein | leer | sichtbare Bildbeschreibung und Alternativtext |
sequence |
Ganzzahl ≥ 0 | nein | nächste freie Position | Reihenfolge in der Diashow |
curl -X POST http://127.0.0.1:47145/api/routes/1/pictures \
-F 'poiId=7' \
-F 'caption=Blick auf die Baumkrone' \
-F 'sequence=0' \
-F 'picture=@eiche.jpg;type=image/jpeg'
Die Antwort enthält Metadaten. Der Location-Header zeigt auf /api/routes/1/pictures/:pictureId.
PUT /api/routes/:id/pictures/:pictureId
Aktualisiert Metadaten und optional die Datei. Der Request kann JSON ohne Datei oder Multipart mit Datei verwenden.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
picture |
Datei | nein | Ersatzdatei |
poiId |
positive Ganzzahl | nein | Bild einem anderen POI derselben Route zuordnen |
caption |
Text | nein | Bildbeschreibung |
sequence |
Ganzzahl ≥ 0 | nein | Reihenfolge |
Nur Metadaten ändern:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pictures/15 \
-H 'Content-Type: application/json' \
-d '{
"poiId": 7,
"caption": "Nahaufnahme der Eichenblätter",
"sequence": 1
}'
Datei und Metadaten ersetzen:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pictures/15 \
-F 'poiId=7' \
-F 'caption=Neue Bildbeschreibung' \
-F 'sequence=1' \
-F 'picture=@eiche-neu.webp;type=image/webp'
DELETE /api/routes/:id/pictures/:pictureId
Löscht den Datenbankeintrag und die einzelne Bilddatei.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pictures/15
8. Audio
Pro POI existiert höchstens eine Audiodatei. Deshalb gibt es keine zusätzliche Audio-ID und keinen Pfad wie /audio/:poiId. Alle Operationen verwenden /api/routes/:id/audio; der konkrete POI wird mit poiId angegeben.
GET /api/routes/:id/audio
Ohne poiId liefert der Endpunkt alle Audiometadaten der Route als JSON:
curl http://127.0.0.1:47145/api/routes/1/audio
Beispielantwort:
{
"audio": [
{
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"url": "/api/routes/1/audio?poiId=7",
"updatedAt": "2026-06-16 12:35:00"
}
]
}
Mit poiId liefert derselbe Endpunkt die Audiodatei binär:
| Query-Parameter | Typ | Pflicht für Dateiausgabe | Beschreibung |
|---|---|---|---|
poiId |
positive Ganzzahl | ja | POI, dessen Audio geliefert wird |
curl --get http://127.0.0.1:47145/api/routes/1/audio \
--data-urlencode 'poiId=7' \
--output ansage.mp3
Der Response-Content-Type entspricht dem gespeicherten Format, beispielsweise audio/mpeg oder audio/ogg.
POST /api/routes/:id/audio
Legt die Audiodatei für einen POI an. Besteht bereits eine Datei, antwortet der Server mit 409; zum Ersetzen wird PUT verwendet.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
audio |
Datei | ja | Audiodatei |
poiId |
positive Ganzzahl | ja | zugehöriger POI |
curl -X POST http://127.0.0.1:47145/api/routes/1/audio \
-F 'poiId=7' \
-F 'audio=@ansage.mp3;type=audio/mpeg'
Der Location-Header lautet beispielsweise:
/api/routes/1/audio?poiId=7
PUT /api/routes/:id/audio
Ersetzt eine vorhandene Audiodatei. poiId darf als Query-Parameter oder als Multipart-Feld angegeben werden.
Query-Variante:
curl -X PUT 'http://127.0.0.1:47145/api/routes/1/audio?poiId=7' \
-F 'audio=@ansage-neu.ogg;type=audio/ogg'
Multipart-Variante:
curl -X PUT http://127.0.0.1:47145/api/routes/1/audio \
-F 'poiId=7' \
-F 'audio=@ansage-neu.ogg;type=audio/ogg'
| Parameter | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
poiId |
Query oder Formular | positive Ganzzahl | ja | POI |
audio |
Formular | Datei | ja | Ersatzdatei |
DELETE /api/routes/:id/audio
Löscht die Audiodatei des angegebenen POIs und setzt audioUrl anschließend auf null.
Empfohlen als Query-Parameter:
curl -X DELETE 'http://127.0.0.1:47145/api/routes/1/audio?poiId=7'
Alternativ als JSON-Body:
curl -X DELETE http://127.0.0.1:47145/api/routes/1/audio \
-H 'Content-Type: application/json' \
-d '{ "poiId": 7 }'
9. Verwendung durch die Clientanwendung
Die Clientanwendung konstruiert Medienadressen ausschließlich über das API-Modul:
Wegwichtel.Api.pictureUrl(routeId, pictureId);
// /api/routes/1/pictures/15
Wegwichtel.Api.audioUrl(routeId, poiId);
// /api/routes/1/audio?poiId=7
Die Diashow setzt die API-Bildadresse als src des Bildes. Der HTML5-Audioplayer setzt die API-Audioadresse als src und lädt sie mit preload="auto". Das Erreichen eines POIs startet keine automatische Wiedergabe.
10. Nginx-Hinweis
Nginx sollte /api/ vollständig an den Node.js-Prozess weiterleiten. Da Bilder und Audio über /api ausgeliefert werden, muss der Proxy binäre Responses unverändert durchreichen:
location ^~ /api/ {
proxy_pass http://127.0.0.1:47145;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
Für Uploads muss außerdem client_max_body_size mindestens so groß wie MAX_UPLOAD_MB gewählt werden.