- eigene Routen mit register_rest_route anlegen
- Parameter deklarieren, prüfen und bereinigen
- Antworten und Fehler sauber zurückgeben
- entscheiden, wann ein eigener Endpunkt nötig ist
Die Standardrouten decken Inhalte ab, aber keine Geschäftslogik. Sobald etwas berechnet, geprüft oder aus mehreren Quellen zusammengeführt werden soll – freie Plätze, ein Preis mit Rabatt, eine Buchung mit Bestätigungsmail –, ist ein eigener Endpunkt der richtige Weg. Diese Lektion zeigt, wie Sie eine Route anlegen, wie Sie Eingaben absichern und wie eine Antwort aussieht, mit der andere Systeme gut arbeiten können.
1. Wann ein eigener Endpunkt?
Blogbeiträge für eine App abrufen
Die Dauer eines Kurses in der Antwort mitliefern
Freie Plätze aus drei Tabellen berechnen und als eine Zahl liefern
Eine Buchung anlegen, dabei Verfügbarkeit prüfen und eine Mail senden
Eine gute Faustregel: Geht es um eine Ressource, die WordPress schon kennt, erweitern Sie die vorhandene Route – mit register_post_meta oder register_rest_field. Geht es um einen Vorgang, also ein Verb wie „buchen“, „prüfen“, „berechnen“, gehört das in eine eigene Route. Das hält die Schnittstelle verständlich und vermeidet Doppelarbeit.
2. Eine Route anlegen
1<?php2add_action( 'rest_api_init', function () {3 register_rest_route( 'radwerk/v1', '/termine/(?P<id>\d+)', [4 'methods' => 'GET',5 'callback' => 'radwerk_termine_lesen',6 'permission_callback' => fn() => current_user_can( 'edit_posts' ),7 'args' => [8 'id' => [9 'validate_callback' => fn( $w ) => is_numeric( $w ) && $w > 0,10 'sanitize_callback' => 'absint',11 ],12 ],13 ] );14} );1516function radwerk_termine_lesen( WP_REST_Request $anfrage ) {17 $id = (int) $anfrage['id'];1819 $treffer = get_transient( "radwerk_termin_$id" );20 if ( false !== $treffer ) {21 return rest_ensure_response( $treffer );22 }2324 $daten = radwerk_termin_daten( $id );25 if ( ! $daten ) {26 return new WP_Error( 'nicht_gefunden', 'Termin unbekannt', [ 'status' => 404 ] );27 }28 set_transient( "radwerk_termin_$id", $daten, 5 * MINUTE_IN_SECONDS );2930 return rest_ensure_response( $daten );31}Der Namensraum verhindert Namenskollisionen und ermöglicht spätere Versionen: Eine radwerk/v2 kann neben v1 bestehen, sodass angebundene Systeme Zeit zum Umstellen haben. Im Routenmuster lassen sich Platzhalter mit regulären Ausdrücken definieren; (?P<id>\d+) bedeutet, dass an dieser Stelle eine Zahl erwartet wird, die später als Parameter id zur Verfügung steht.
3. Eingaben und Antworten
Jeder Parameter sollte in args deklariert werden. WordPress prüft dann selbst, ob Pflichtfelder vorhanden sind, ob der Typ stimmt und ob der Wert die Prüfung besteht – und antwortet andernfalls mit 400 und einer verständlichen Meldung, bevor Ihr Code überhaupt läuft. Für Antworten gilt: rest_ensure_response liefert ein sauberes Objekt, WP_Error mit Statuscode einen sauberen Fehler. Eigene Header, etwa für Gesamtzahlen bei Listen, lassen sich ergänzen.
1<?php2function radwerk_termine_liste( WP_REST_Request $anfrage ) {3 $seite = (int) $anfrage->get_param( 'page' );4 $treffer = radwerk_termine_suchen( $seite );56 $antwort = rest_ensure_response( $treffer['eintraege'] );7 $antwort->header( 'X-WP-Total', (string) $treffer['gesamt'] );8 $antwort->header( 'X-WP-TotalPages', (string) ceil( $treffer['gesamt'] / 20 ) );910 return $antwort;11}1213// Schema beschreibt die Antwort – erscheint in der Selbstbeschreibung der API14'schema' => fn() => [15 'title' => 'termin',16 'type' => 'object',17 'properties' => [18 'id' => [ 'type' => 'integer' ],19 'titel' => [ 'type' => 'string' ],20 'frei' => [ 'type' => 'integer', 'description' => 'freie Plätze' ],21 ],22],1GET /wp-json/radwerk/v1/termine/abc23// In der Route deklariert:4'id' => [ 'validate_callback' => fn( $w ) => is_numeric( $w ) ]4. Vorhandene Routen erweitern
Oft genügt es, eine Standardroute um eigene Felder zu ergänzen. register_rest_field fügt der Antwort ein berechnetes Feld hinzu, etwa die Lesezeit eines Beitrags oder die freien Plätze eines Kurses. Der Vorteil: Aufrufende arbeiten weiter mit der gewohnten Route, und alle Parameter wie _fields oder _embed funktionieren unverändert.
1<?php2add_action( 'rest_api_init', function () {3 register_rest_field( 'radwerk_termin', 'freie_plaetze', [4 'get_callback' => fn( $beitrag ) => radwerk_freie_plaetze( $beitrag['id'] ),5 'schema' => [ 'type' => 'integer', 'context' => [ 'view' ] ],6 ] );7} );5. Leistung und Stabilität
Eigene Endpunkte werden oft häufiger aufgerufen als gedacht – von einer App bei jedem Bildschirmwechsel, von einem Automatisierungsdienst im Minutentakt. Drei Maßnahmen halten sie stabil. Erstens Zwischenspeichern: Ergebnisse, die sich selten ändern, gehören in einen Transient mit kurzer Laufzeit. Zweitens Begrenzen: Listen immer seitenweise ausliefern, nie alles auf einmal. Drittens Abfangen: Wenn der Endpunkt selbst einen externen Dienst aufruft, braucht dieser Aufruf ein Zeitlimit und einen Rückfallwert, sonst hängt bei einer Störung die ganze Schnittstelle.
Zur Stabilität gehört auch eine kurze Dokumentation. Für jede eigene Route sollte festgehalten sein: Adresse, Methode, benötigte Rechte, Parameter mit Typ, Beispielantwort und mögliche Fehler. Ein Schema in der Registrierung erzeugt einen Teil davon automatisch – die Selbstbeschreibung unter /wp-json/radwerk/v1 zeigt dann alle Routen mit ihren Parametern.
6. Ein Beispiel
Ein Yogastudio hat eine App, in der Mitglieder Kurse buchen. Dafür gibt es drei eigene Routen: GET /kurse liefert die Kurse einer Woche mit freien Plätzen, POST /buchungen legt eine Buchung an und DELETE /buchungen/{id} storniert sie. Die Leseroute ist öffentlich und zwischengespeichert, die beiden anderen verlangen eine Anmeldung. Die Buchungsroute prüft in einem Schritt, ob der Kurs existiert, ob Plätze frei sind und ob nicht schon eine Buchung vorliegt; je nach Ergebnis antwortet sie mit 201, 409 oder 403. Die App muss dadurch keine Regeln kennen – sie stellt eine Anfrage und wertet den Statuscode aus.
7. Häufige Fragen
Warum bekomme ich 404 auf meine eigene Route? Meist wurde sie nicht auf rest_api_init registriert, oder das Plugin ist nicht aktiv. Prüfen Sie außerdem die Selbstbeschreibung unter /wp-json – erscheint Ihr Namensraum dort nicht, wird die Registrierung gar nicht ausgeführt.
Wie teste ich Endpunkte? Für lesende Routen genügt der Browser. Für schreibende eignen sich Werkzeuge wie Hoppscotch, Insomnia oder curl. In Tests lässt sich mit der Klasse WP_REST_Request eine Anfrage direkt im PHPUnit-Test auslösen, ohne HTTP.
Brauche ich für jede Route eine eigene Klasse? Nein, aber Struktur hilft: eine Klasse je Themenbereich mit einer Methode register_routes, die alle zugehörigen Routen anmeldet. So bleibt nachvollziehbar, welche Schnittstellen ein Plugin anbietet.
8. Übung zum Selbermachen
Legen Sie in Ihrem Übungsplugin die Route uebung/v1/statistik an, die die Anzahl veröffentlichter Beiträge, Seiten und Kommentare als JSON liefert – öffentlich lesbar. Ergänzen Sie danach eine zweite Route uebung/v1/notiz, die per POST einen Text entgegennimmt und in einer Option speichert; sie darf nur mit der Berechtigung manage_options ausführbar sein. Testen Sie beide Routen einmal angemeldet und einmal in einem privaten Fenster und vergleichen Sie die Statuscodes.
- [1]WordPress.org (2026): REST API Handbook – Adding Custom Endpoints, Routes and Endpoints, Schema.
- [2]WordPress.org (2026): Code Reference – register_rest_route(), register_rest_field(), rest_ensure_response().
- [3]WordPress.org (2026): Plugin Security – Securing REST API Endpoints.
Stand: September 2026, WordPress 7.1, PHP 8.3. Kursmaterial der Klarwerk Akademie. WordPress entwickelt sich schnell – maßgeblich ist die aktuelle Dokumentation auf developer.wordpress.org.