20 KiB
Wegwichtel REST-API
Diese Datei dokumentiert die vollständige HTTP-Schnittstelle des Wegwichtel-Servers. Sämtliche JSON-Ressourcen und aktiven GPX-, Bild- und Audiodateien werden unter /api bereitgestellt. Ein separates öffentliches /media-URL-Schema wird nicht verwendet.
1. Grundlagen
Basisadressen
Lokaler Standard:
http://127.0.0.1:47145
API-Basis:
http://127.0.0.1:47145/api
Bei einer Nginx-Installation bleibt der Pfad gleich, beispielsweise:
https://wegwichtel.example.org/api
Formate
- Listen- und Metadatenzugriffe liefern JSON.
- Die Einzelendpunkte für GPX, Bilder und Audio liefern die jeweilige Datei direkt aus. Mit
?metadata=trueliefern die Bild- und Audioendpunkte stattdessen JSON-Metadaten. - Schreibzugriffe ohne Dateien akzeptieren JSON,
application/x-www-form-urlencodedodermultipart/form-data. - Schreibzugriffe mit Dateien verwenden
multipart/form-data. - Pro Medien-Request wird genau eine Bild- beziehungsweise Audiodatei verarbeitet.
- Zeitstempel werden als SQLite- oder ISO-8601-Text ausgegeben.
- Die aktuelle API besitzt keine Authentifizierung. Schreibzugriffe dürfen nicht ungeschützt öffentlich erreichbar sein.
Uploadgrenzen und Dateitypen
Das Dateilimit pro Datei wird durch MAX_UPLOAD_MB festgelegt und beträgt standardmäßig 50 MB.
Unterstützte Dateitypen:
| Ressource | Dateiendungen beziehungsweise MIME-Typen |
|---|---|
| GPX | .gpx, application/gpx+xml, application/xml, text/xml |
| Bilder | JPEG, PNG, WebP |
| Audio | MP3, MP4/M4A, AAC, Ogg, WAV, WebM |
Die Uploadfelder heißen:
| Ressource | Feldname |
|---|---|
| GPX | gpx |
| einzelnes Bild | picture |
| einzelne Audiodatei | audio |
Fehlerformat
{
"error": "HttpError",
"message": "Strecke nicht gefunden."
}
Typische Statuscodes:
| Status | Bedeutung |
|---|---|
200 |
Anfrage erfolgreich |
201 |
Ressource wurde angelegt |
400 |
Parameter oder Upload fehlt beziehungsweise ist ungültig |
404 |
Route, POI oder Medienressource wurde nicht gefunden |
409 |
Ressource existiert bereits oder Dateisystemzustand verhindert die Operation |
413 |
Datei überschreitet MAX_UPLOAD_MB |
415 |
Dateityp wird nicht unterstützt |
500 |
Interner Serverfehler |
IDs
Alle IDs sind positive, von SQLite erzeugte Ganzzahlen.
:idbezeichnet bei/routes/:id/...die Route.:pictureIdbezeichnet einen Datensatz auspoi_images.:poiIdbezeichnet den POI und zugleich seine höchstens eine Audioressource.
Beispiele verwenden überwiegend 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": "/api/routes/1/gpx",
"createdAt": "2026-06-16 12:00:00",
"updatedAt": "2026-06-16 12:00:00",
"deletedAt": null,
"proximityM": 324.8
}
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/pois/7/audio",
"images": [
{
"id": 15,
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/pictures/15"
}
]
}
Bildressource
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/pictures/15",
"createdAt": "2026-06-16 12:30:00"
}
Audioressource
Pro POI kann höchstens eine Audiodatei existieren. Deshalb wird die Audioressource über die poiId adressiert.
{
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"url": "/api/routes/1/pois/7/audio",
"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 |
GET |
/api/routes/:id/gpx |
GPX-Datei der Route ausliefern |
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-Metadaten anlegen |
PUT |
/api/pois/:id |
POI-Metadaten aktualisieren |
GET |
/api/routes/:routeId/pois/:poiId/pictures |
Bilder eines POIs auflisten |
GET |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
Bilddatei ausliefern; optional Metadaten mit ?metadata=true |
POST |
/api/routes/:routeId/pois/:poiId/pictures |
einzelnes Bild für den POI hochladen |
PUT |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
Bilddatei oder Metadaten aktualisieren |
DELETE |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
einzelnes Bild löschen |
GET |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei ausliefern; optional Metadaten mit ?metadata=true |
POST |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei für den POI anlegen |
PUT |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei des POIs ersetzen |
DELETE |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei des POIs löschen |
4. Systemzustand
GET /api/health
Parameter: keine.
curl http://127.0.0.1:47145/api/health
{
"ok": true,
"service": "wegwichtel",
"socket": "127.0.0.1:47145",
"sqliteVersion": "3.46.1",
"timestamp": "2026-06-16T12:00:00.000Z"
}
5. Routen lesen
GET /api/routes
Ohne Positionsparameter werden alle aktiven Routen alphabetisch geliefert. Mit lat und lon wird die Entfernung zum Routenstart berechnet, anhand radiusKm gefiltert und nach Entfernung sortiert.
Query-Parameter
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
lat |
Dezimalzahl | nein | – | Breitengrad; nur zusammen mit lon wirksam |
lon |
Dezimalzahl | nein | – | Längengrad; nur zusammen mit lat wirksam |
radiusKm |
Dezimalzahl | nein | DEFAULT_ROUTE_RADIUS_KM, standardmäßig 25 |
maximaler Abstand zum Routenstart |
includeDeleted |
Boolean-Text | nein | false |
der exakte Wert true schließt gelöschte Routen ein |
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
Liefert Route, GPX-Punkte und POIs einschließlich Medien-URLs.
Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
id |
Ganzzahl | ja | Route |
Query-Parameter
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
includeDeleted |
Boolean-Text | nein | false |
mit true kann eine gelöschte Route gelesen werden |
curl http://127.0.0.1:47145/api/routes/1
curl 'http://127.0.0.1:47145/api/routes/1?includeDeleted=true'
GET /api/routes/:id/gpx
Liefert die aktive GPX-Datei der Route direkt mit Content-Type: application/gpx+xml aus. Der in einer Routenressource enthaltene Wert gpxUrl verweist auf diesen Endpunkt.
curl http://127.0.0.1:47145/api/routes/1/gpx \
--output schulwald-runde.gpx
Parameter außer der Routen-ID sind nicht vorgesehen. Gelöschte oder unbekannte Routen antworten mit 404.
6. Route anlegen und bearbeiten
POST /api/routes
Legt eine Route aus einer GPX-Datei an.
Content-Type: multipart/form-data
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
gpx |
Datei | ja | – | GPX-Datei mit mindestens einem Trackpunkt |
name |
Text | bedingt | Name aus GPX | erforderlich, wenn GPX keinen Namen enthält |
slug |
Text | nein | aus name |
interne URL-freundliche Kennung |
description |
Text | nein | leer | Routenbeschreibung |
schoolName |
Text | nein | leer | Schule oder Einrichtung |
curl -X POST http://127.0.0.1:47145/api/routes \
-F 'name=Schulwald-Runde' \
-F 'slug=schulwald-runde-klasse-7a' \
-F 'description=Naturkundlicher Rundweg der Klasse 7a' \
-F 'schoolName=Beispielschule' \
-F 'gpx=@examples/sample-route.gpx;type=application/gpx+xml'
Erfolg: 201 Created und Location: /api/routes/<id>.
PUT /api/routes/:id
Aktualisiert Metadaten. Eine optionale GPX-Datei ersetzt alle bisherigen Trackpunkte.
| Feld | Typ | Pflicht | Verhalten ohne Feld |
|---|---|---|---|
gpx |
Datei | nein | bisherige GPX-Punkte bleiben erhalten |
name |
Text | nein | bisheriger Wert bleibt |
description |
Text | nein | bisheriger Wert bleibt |
schoolName |
Text | nein | bisheriger Wert bleibt |
curl -X PUT http://127.0.0.1:47145/api/routes/1 \
-F 'name=Schulwald-Runde 2026' \
-F 'description=Überarbeitete Strecke' \
-F 'schoolName=Beispielschule' \
-F 'gpx=@route-neu.gpx;type=application/gpx+xml'
POST /api/routes/:id/append
Hängt alle Trackpunkte einer GPX-Datei an die Route an und berechnet Streckenwerte neu.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
gpx |
Datei | ja | anzuhängende GPX-Datei |
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
-F 'gpx=@verlaengerung.gpx;type=application/gpx+xml'
7. POIs lesen und bearbeiten
GET /api/routes/:id/pois
Liefert alle POIs einer aktiven Route nach sequence und id.
curl http://127.0.0.1:47145/api/routes/1/pois
GET /api/pois/:id
Liefert einen einzelnen POI einschließlich seiner aktuellen Bild- und Audio-URLs.
curl http://127.0.0.1:47145/api/pois/7
POST /api/routes/:id/pois
Legt ausschließlich die POI-Metadaten an. Bilder und Audio werden anschließend über die gesonderten Medienendpunkte hochgeladen.
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
title |
Text | nein | Unbenannter POI |
Stationsname |
description |
Text | nein | leer | Beschreibung |
lat |
Dezimalzahl | ja | – | Breitengrad |
lon |
Dezimalzahl | ja | – | Längengrad |
triggerRadiusM |
Dezimalzahl | nein | DEFAULT_POI_TRIGGER_METERS, standardmäßig 80 |
Aktivierungsradius in Metern |
sequence |
Ganzzahl | nein | 0 |
Reihenfolge in der Stationsliste |
JSON-Beispiel mit allen Parametern:
curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
-H 'Content-Type: application/json' \
-d '{
"title": "Die alte Eiche",
"description": "Hier wird das Alter der Eiche erklärt.",
"lat": 52.5208,
"lon": 13.4070,
"triggerRadiusM": 60,
"sequence": 2
}'
Erfolg: 201 Created und Location: /api/pois/<id>.
PUT /api/pois/:id
Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erhalten.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
title |
Text | nein | neuer Stationsname |
description |
Text | nein | neue Beschreibung |
lat |
Dezimalzahl | nein | neuer Breitengrad |
lon |
Dezimalzahl | nein | neuer Längengrad |
triggerRadiusM |
Dezimalzahl | nein | neuer Aktivierungsradius |
sequence |
nichtnegative Ganzzahl | nein | neue Reihenfolge |
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": 3
}'
8. Bilder eines POIs einzeln verwalten
Die Route und der POI sind Bestandteil jedes Bildpfades. Dadurch ist die Zuordnung eindeutig und beim Upload muss keine zusätzliche poiId übergeben werden.
Gemeinsame Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
positive Ganzzahl | ja | ID der aktiven Route |
poiId |
positive Ganzzahl | ja | ID eines POIs, der zu dieser Route gehört |
pictureId |
positive Ganzzahl | nur bei Einzelressourcen | ID des Bildes, das zu diesem POI gehört |
GET /api/routes/:routeId/pois/:poiId/pictures
Liefert alle Bilder des angegebenen POIs in Diashow-Reihenfolge.
curl http://127.0.0.1:47145/api/routes/1/pois/7/pictures
{
"pictures": [
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/pictures/15",
"createdAt": "2026-06-16 12:30:00"
}
]
}
GET /api/routes/:routeId/pois/:poiId/pictures/:pictureId
Liefert standardmäßig die Bilddatei direkt aus. Genau dieser Pfad wird im Feld url der Bildressource und unter pois[].images[].url ausgegeben.
Bild speichern:
curl http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
--output bild-15.jpg
Metadaten statt Dateidaten abrufen:
curl --get http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
--data-urlencode 'metadata=true'
| Query-Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
metadata |
Boolean | nein | false |
bei true JSON-Metadaten statt der Bilddatei liefern |
Die API antwortet mit 404, wenn Route, POI oder Bild nicht zusammengehören.
POST /api/routes/:routeId/pois/:poiId/pictures
Lädt genau ein Bild für den im Pfad angegebenen POI hoch.
Content-Type: multipart/form-data
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
picture |
Datei | ja | – | JPEG-, PNG- oder WebP-Datei |
caption |
Text | nein | leer | sichtbare Bildbeschreibung und Grundlage für den Alternativtext |
sequence |
nichtnegative Ganzzahl | nein | nächster freier Wert des POIs | Reihenfolge in der Diashow |
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/pictures \
-F 'caption=Blick auf die Baumkrone' \
-F 'sequence=0' \
-F 'picture=@eiche.jpg;type=image/jpeg'
Erfolg: 201 Created und Location: /api/routes/1/pois/7/pictures/<pictureId>.
PUT /api/routes/:routeId/pois/:poiId/pictures/:pictureId
Aktualisiert Metadaten und kann optional die Datei ersetzen. Nicht übergebene Metadaten bleiben erhalten. Ein Bild kann über diesen Endpunkt nicht einem anderen POI zugeordnet werden; dafür muss es beim bisherigen POI gelöscht und beim Ziel-POI neu angelegt werden.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
picture |
Datei | nein | ersetzt die bisherige Bilddatei |
caption |
Text | nein | neue Bildbeschreibung; leerer Text entfernt die Beschreibung |
sequence |
nichtnegative Ganzzahl | nein | neue Position in der Diashow |
Nur Metadaten per JSON ändern:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-H 'Content-Type: application/json' \
-d '{
"caption": "Nahaufnahme der Eichenblätter",
"sequence": 1
}'
Datei und Metadaten ersetzen:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-F 'caption=Neue Aufnahme der Eiche' \
-F 'sequence=2' \
-F 'picture=@eiche-neu.webp;type=image/webp'
Wird eine Datei ersetzt, entfernt der Server die bisherige Datei nach erfolgreicher Datenbankaktualisierung.
DELETE /api/routes/:routeId/pois/:poiId/pictures/:pictureId
Entfernt Bilddatensatz und Datei.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15
{
"id": 15,
"routeId": 1,
"poiId": 7,
"deleted": true
}
9. Audiodatei eines POIs verwalten
Pro POI ist höchstens eine Audiodatei vorgesehen. Daher ist /audio selbst die Einzelressource; eine zusätzliche Audio-ID ist nicht erforderlich.
Gemeinsame Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
positive Ganzzahl | ja | ID der aktiven Route |
poiId |
positive Ganzzahl | ja | ID eines POIs, der zu dieser Route gehört |
GET /api/routes/:routeId/pois/:poiId/audio
Liefert standardmäßig die Audiodatei des POIs direkt aus. Genau dieser Pfad wird in audioUrl und im Feld url der Audioressource ausgegeben. Der Endpunkt unterstützt HTTP-Range-Anfragen, damit Browser innerhalb der Audiodatei springen können.
Audiodatei speichern:
curl http://127.0.0.1:47145/api/routes/1/pois/7/audio \
--output ansage-7.mp3
Metadaten statt Dateidaten abrufen:
curl --get http://127.0.0.1:47145/api/routes/1/pois/7/audio \
--data-urlencode 'metadata=true'
| Query-Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
metadata |
Boolean | nein | false |
bei true JSON-Metadaten statt der Audiodatei liefern |
Antwortet mit 404, wenn Route und POI nicht zusammengehören oder der POI keine Audiodatei besitzt.
POST /api/routes/:routeId/pois/:poiId/audio
Legt die Audiodatei des im Pfad angegebenen POIs an.
Content-Type: multipart/form-data
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
audio |
Datei | ja | MP3-, MP4/M4A-, AAC-, Ogg-, WAV- oder WebM-Datei |
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-F 'audio=@ansage.mp3;type=audio/mpeg'
Erfolg: 201 Created und Location: /api/routes/1/pois/7/audio.
Existiert bereits eine Audiodatei, antwortet der Server mit 409. Zum Ersetzen ist PUT zu verwenden.
PUT /api/routes/:routeId/pois/:poiId/audio
Ersetzt die vorhandene Audiodatei des POIs.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
audio |
Datei | ja | neue Audiodatei |
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-F 'audio=@ansage-neu.ogg;type=audio/ogg'
Der Server entfernt die bisherige Datei nach erfolgreicher Aktualisierung. Besitzt der POI noch keine Audiodatei, antwortet der Server mit 404; zum erstmaligen Anlegen ist POST zu verwenden.
DELETE /api/routes/:routeId/pois/:poiId/audio
Entfernt die Audiodatei und setzt audioUrl des POIs auf null.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7/audio
{
"routeId": 1,
"poiId": 7,
"deleted": true
}
10. Route weich löschen und wiederherstellen
DELETE /api/routes/:id
Markiert die Route als gelöscht und verschiebt das vollständige Streckenverzeichnis mit GPX, Bildern und Audio nach storage/trash/routes/. Die Datenbankeinträge bleiben erhalten und ihre Pfade werden auf den Papierkorb umgeschrieben.
curl -X DELETE http://127.0.0.1:47145/api/routes/1
{
"route": {
"id": 1,
"status": "deleted",
"gpxUrl": null
},
"softDeleted": true
}
POST /api/routes/:id/restore
Verschiebt eine gelöschte Route zurück in den aktiven Speicher und schreibt alle GPX-, Bild- und Audiopfade zurück.
Parameter: keine.
curl -X POST http://127.0.0.1:47145/api/routes/1/restore
11. Dateien über REST ausliefern
Die API veröffentlicht keine internen Speicherpfade und keine /media/...-Adressen. Alle in JSON ausgegebenen Datei-URLs verweisen auf stabile REST-Endpunkte:
GET /api/routes/1/gpx
GET /api/routes/1/pois/7/pictures/15
GET /api/routes/1/pois/7/audio
Die Zuordnung lautet:
| JSON-Feld | Datei-Endpunkt |
|---|---|
route.gpxUrl |
/api/routes/:id/gpx |
poi.audioUrl |
/api/routes/:routeId/pois/:poiId/audio |
poi.images[].url |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
picture.url |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
audio.url |
/api/routes/:routeId/pois/:poiId/audio |
Die Dateinamen und relativen Pfade unter storage/ bleiben ausschließlich interne Implementierungsdetails. Dateien gelöschter Routen liegen im Papierkorb und sind über keinen Datei-Endpunkt erreichbar.