Menu
CoddyTech

Versionierung

Teil des Abschnitts REST-APIs der Node.js-Journey von Coddy. Lektion 22 von 25.

Eine Änderung der Antwortstruktur bringt jeden Client zum Scheitern, der sie bereits verwendet. Eine Version im Pfad ermöglicht es alten und neuen Clients, parallel zu bestehen: heute /api/v1/orders, /api/v2/orders, wenn sich die Struktur ändert. Wenn ein Pfad verschoben wird, antwortet der alte Pfad mit einer Weiterleitung zum neuen:

GET  /api/orders/5   →  308  Location: /api/v1/orders/5
POST /api/orders     →  308  Location: /api/v1/orders

Bei 301 und 302 können Clients einen POST als GET wiederholen, wodurch der Body verloren geht. 308 bedeutet: „Wiederhole dieselbe Anfrage dort“, einschließlich Methode und Body. Innerhalb einer eingebundenen Middleware hat req.url den Einbindungspfad verloren, während req.originalUrl ihn vollständig beibehält:

// in app.use('/api/orders', ...) für GET /api/orders/5?full=1
req.url          // '/5?full=1'
req.originalUrl  // '/api/orders/5?full=1'
challenge icon

Aufgabe

Einfach

Deine API wird sich auf eine Weise ändern, die alte Clients nicht erwarten, deshalb solltest du eine Versionsnummer in ihre Pfade aufnehmen. Die heutige API wird zu Version 1 unter /api/v1/tasks, und die alten Adressen leiten Clients dorthin weiter.

  1. Binde in server.js den Tasks-Router unter /api/v1/tasks statt unter /api/tasks ein.
  2. Füge ebenfalls in server.js eine Middleware hinzu, die unter /api/tasks eingebunden ist und ausschließlich weiterleitet. Sie antwortet mit dem Status 308 und leitet an dieselbe Adresse unter /api/v1 weiter, wobei der restliche Pfad und der Query-String erhalten bleiben. Innerhalb einer eingebundenen Middleware enthält req.originalUrl die vollständige alte Adresse. Zum Beispiel:
    • GET /api/tasks/2 leitet weiter zu /api/v1/tasks/2.
    • POST /api/tasks leitet weiter zu /api/v1/tasks.
    • GET /api/tasks?done=false&sort=title leitet weiter zu /api/v1/tasks?done=false&sort=title.

    Verwende 308, nicht 301: Nach einem 308 wiederholt ein Client einen POST als POST, einschließlich des Bodys.

  3. In routes/tasks.js setzt die POST-Route Location auf die neue Adresse, zum Beispiel /api/v1/tasks/4 für Aufgabe 4.

Probiere es aus: GET /api/tasks/2 gibt 308 zurück, und im Tab „Headers“ wird Location: /api/v1/tasks/2 angezeigt. Die Anfrageleiste zeigt die Weiterleitung selbst; sie folgt ihr nicht. Sende POST mit { "title": "Order new chairs" } an /api/tasks: Du erhältst 308 mit Location: /api/v1/tasks, und es wird keine Aufgabe erstellt. Sende denselben Body per POST an /api/v1/tasks: 201 mit Location: /api/v1/tasks/4. GET /api/v1/tasks/2 gibt Aufgabe 2 zurück.

Probier es selbst

import express from 'express';
import { sendError } from './errors.js';
import taskRoutes from './routes/tasks.js';

const BOARD_ORIGIN = 'https://board.example.com';

const app = express();

// Ermöglicht Seiten auf BOARD_ORIGIN, diese API vom Browser aus aufzurufen.
app.use((req, res, next) => {
    res.set('Access-Control-Allow-Origin', BOARD_ORIGIN);

    if (req.method === 'OPTIONS') {
        res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
        res.set('Access-Control-Allow-Headers', 'Content-Type');
        return res.status(204).end();
    }
    next();
});

app.use(express.json());

app.use(express.static('public'));

app.use('/api/tasks', taskRoutes);

// Fehler aus den Routen landen hier. Ein Body, der kein gültiges JSON enthält, ist ein
// Fehler des Clients (400); alles andere ist unser Fehler (500).
app.use((err, req, res, next) => {
    if (err.type === 'entity.parse.failed') {
        return sendError(res, 400, 'invalid_json', 'The body is not valid JSON');
    }
    console.error(err);
    sendError(res, 500, 'server_error', 'Something went wrong');
});

app.listen(3000, () => {
    console.log('Task tracker API on port 3000');
});

Alle Lektionen in REST-APIs

Übe selbstständig: Express-Playground