Menu
CoddyTech

Gestion des versions

Fait partie de la section API REST du Journey Node.js de Coddy. Leçon 22 sur 25.

Modifier la structure d’une réponse perturbe tous les clients qui l’utilisent déjà. Une version dans le chemin permet aux anciens et aux nouveaux clients de coexister : /api/v1/orders aujourd’hui, /api/v2/orders lorsque la structure change. Lorsqu’un chemin est déplacé, l’ancien chemin répond par une redirection vers le nouveau :

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

Les codes 301 et 302 permettent aux clients de répéter une requête POST sous forme de GET, ce qui supprime le corps. 308 signifie « répétez la même requête à cet endroit », méthode et corps compris. Dans un middleware monté, req.url ne contient plus le chemin de montage, tandis que req.originalUrl le conserve en entier :

// dans app.use('/api/orders', ...) pour GET /api/orders/5?full=1
req.url          // '/5?full=1'
req.originalUrl  // '/api/orders/5?full=1'
challenge icon

Défi

Facile

Votre API va évoluer d’une manière inattendue pour les anciens clients ; ajoutez donc un numéro de version à ses chemins. L’API actuelle devient la version 1, à l’adresse /api/v1/tasks, et les anciennes adresses y redirigent les clients.

  1. Dans server.js, montez le routeur des tâches sur /api/v1/tasks au lieu de /api/tasks.
  2. Toujours dans server.js, ajoutez un intergiciel monté sur /api/tasks qui ne fait que rediriger. Il répond avec le statut 308 vers la même adresse sous /api/v1, en conservant le reste du chemin et la chaîne de requête. Dans un intergiciel monté, req.originalUrl contient l’ancienne adresse complète. Par exemple :
    • GET /api/tasks/2 redirige vers /api/v1/tasks/2.
    • POST /api/tasks redirige vers /api/v1/tasks.
    • GET /api/tasks?done=false&sort=title redirige vers /api/v1/tasks?done=false&sort=title.

    Utilisez 308, et non 301 : après une réponse 308, le client répète une requête POST en POST, corps inclus.

  3. Dans routes/tasks.js, la route POST définit Location sur la nouvelle adresse, par exemple /api/v1/tasks/4 pour la tâche 4.

Essayez : GET /api/tasks/2 renvoie 308, et l’onglet Headers affiche Location: /api/v1/tasks/2. La barre de requête affiche la redirection elle-même ; elle ne la suit pas. Envoyez POST { "title": "Order new chairs" } à /api/tasks : vous obtenez 308 avec Location: /api/v1/tasks, et aucune tâche n’est créée. Envoyez le même corps avec POST à /api/v1/tasks : 201 avec Location: /api/v1/tasks/4. GET /api/v1/tasks/2 renvoie la tâche 2.

Essayez vous-même

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();

// Permet aux pages de BOARD_ORIGIN d’appeler cette API depuis le navigateur.
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);

// Les erreurs provenant des routes arrivent ici. Un corps qui n’est pas du JSON valide est une
// erreur du client (400) ; toute autre erreur vient de nous (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');
});

Toutes les leçons de API REST

Entraînez-vous par vous-même : Playground Express