Ce que vous saurez faire à la fin
- Comprendre les principes REST en 10 minutes
- Consommer une API publique depuis terminal, Postman, Node
- Gérer authentification, pagination, erreurs
- Construire un petit serveur REST Express en 20 lignes
- Tester et documenter votre API
Durée : 2 heures. Pré-requis : Node.js 20+, connaissance JSON.
Étape 1 — Vocabulaire REST
Ressource : "client", "facture", "produit" (nom commun pluriel)
Endpoint : URL associée, ex: /clients, /clients/42
Verbe HTTP : action sur la ressource
GET lire
POST créer
PUT remplacer entièrement
PATCH modifier partiellement
DELETE supprimer
Status code : réponse serveur (200 OK, 404 Not Found, 500 Error...)
Étape 2 — Tester une API publique avec curl
# API JSONPlaceholder (gratuite, pour tests)
curl https://jsonplaceholder.typicode.com/users/1
# Lire avec jq (parseur JSON)
curl -s https://jsonplaceholder.typicode.com/users/1 | jq .
# Créer
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title":"Mon post","body":"Contenu","userId":1}'
# Supprimer
curl -X DELETE https://jsonplaceholder.typicode.com/posts/1
Étape 3 — Postman pour les tests interactifs
- Téléchargez Postman (
postman.com/downloads). - Créez une collection « Test API ».
- Ajoutez une requête : GET
https://jsonplaceholder.typicode.com/users. - Onglet Headers : ajoutez
Accept: application/jsonsi besoin. - Send : vous voyez la réponse, status code, temps, taille.
- Sauvegardez comme « List users ».
- Dupliquez pour GET/POST/PUT/DELETE. Votre collection devient un cahier de tests.
Étape 4 — Client JavaScript avec fetch
const API = "https://jsonplaceholder.typicode.com";
async function getUser(id) {
const r = await fetch(`${API}/users/${id}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
async function creerPost(data) {
const r = await fetch(`${API}/posts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
return r.json();
}
// Utilisation
const user = await getUser(1);
console.log(user.name);
const post = await creerPost({ title: "Salut", body: "Test", userId: 1 });
console.log(post.id);
Étape 5 — Gérer l’authentification
// Token dans le header Authorization
const TOKEN = process.env.API_TOKEN;
async function getFactures() {
const r = await fetch("https://api.example.sn/v1/factures", {
headers: { "Authorization": `Bearer ${TOKEN}` },
});
if (r.status === 401) throw new Error("Token invalide");
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
# Avec curl
curl -s "https://api.example.sn/v1/factures" \
-H "Authorization: Bearer $TOKEN" \
| jq '.data[] | {numero, montant}'
Étape 6 — Pagination
async function* paginerTous(url) {
let next = url;
while (next) {
const r = await fetch(next).then(r => r.json());
yield* r.data;
next = r.links?.next;
}
}
// Usage
for await (const client of paginerTous("https://api.example.sn/v1/clients?limit=100")) {
console.log(client.nom);
}
Étape 7 — Retries et backoff
async function withRetry(fn, max = 3) {
for (let i = 0; i < max; i++) {
try {
return await fn();
} catch (e) {
if (e.status === 429 || (e.status >= 500 && e.status < 600)) {
const backoff = 2 ** i * 500 + Math.random() * 300;
await new Promise(r => setTimeout(r, backoff));
continue;
}
throw e;
}
}
throw new Error("Max retries dépassé");
}
const data = await withRetry(() => getFactures());
Étape 8 — Idempotence pour POSTs critiques
import { randomUUID } from "crypto";
await fetch("https://api.example.sn/v1/paiements", {
method: "POST",
headers: {
"Idempotency-Key": randomUUID(), // unique par paiement
"Content-Type": "application/json",
"Authorization": `Bearer ${TOKEN}`,
},
body: JSON.stringify({ montant: 150000, client_id: 42 }),
});
Étape 9 — Construire votre première API avec Express
mkdir mon-api && cd mon-api
npm init -y
npm install express
// server.js
import express from "express";
const app = express();
app.use(express.json());
const clients = new Map();
let seq = 1;
// Créer
app.post("/v1/clients", (req, res) => {
if (!req.body.nom) return res.status(400).json({ error: "nom requis" });
const id = String(seq++);
const c = { id, nom: req.body.nom, ville: req.body.ville || "" };
clients.set(id, c);
res.status(201).location(`/v1/clients/${id}`).json(c);
});
// Lister
app.get("/v1/clients", (_req, res) => {
res.json({ data: [...clients.values()] });
});
// Lire un
app.get("/v1/clients/:id", (req, res) => {
const c = clients.get(req.params.id);
return c ? res.json(c) : res.status(404).json({ error: "not_found" });
});
// Modifier
app.patch("/v1/clients/:id", (req, res) => {
const c = clients.get(req.params.id);
if (!c) return res.status(404).json({ error: "not_found" });
Object.assign(c, req.body);
res.json(c);
});
// Supprimer
app.delete("/v1/clients/:id", (req, res) => {
clients.delete(req.params.id);
res.status(204).end();
});
app.listen(3000, () => console.log("API http://localhost:3000"));
node server.js
# Tester:
curl -X POST http://localhost:3000/v1/clients \
-H "Content-Type: application/json" \
-d '{"nom":"SARL Dakar","ville":"Dakar"}'
curl http://localhost:3000/v1/clients
Étape 10 — Validation avec Zod
npm install zod
import { z } from "zod";
const CreerClient = z.object({
nom: z.string().min(2).max(100),
ville: z.string().optional(),
email: z.string().email().optional(),
});
app.post("/v1/clients", (req, res) => {
const result = CreerClient.safeParse(req.body);
if (!result.success) {
return res.status(422).json({ errors: result.error.errors });
}
// ... use result.data
});
Étape 11 — CORS
npm install cors
import cors from "cors";
app.use(cors({
origin: ["https://app.example.sn"],
credentials: true,
}));
Étape 12 — Rate limiting
npm install express-rate-limit
import { rateLimit } from "express-rate-limit";
app.use("/v1/", rateLimit({
windowMs: 60_000,
max: 120,
standardHeaders: true,
legacyHeaders: false,
}));
Étape 13 — Tests avec Supertest
npm install -D supertest vitest
// server.test.js
import request from "supertest";
import app from "./server.js";
test("POST /v1/clients crée un client", async () => {
const r = await request(app)
.post("/v1/clients")
.send({ nom: "Test" })
.expect(201);
expect(r.body).toHaveProperty("id");
});
test("GET /v1/clients/:id inexistant renvoie 404", async () => {
await request(app).get("/v1/clients/9999").expect(404);
});
Étape 14 — Documenter avec OpenAPI
npm install swagger-ui-express swagger-jsdoc
import swaggerUi from "swagger-ui-express";
import swaggerJsdoc from "swagger-jsdoc";
const swaggerSpec = swaggerJsdoc({
definition: {
openapi: "3.0.0",
info: { title: "Mon API", version: "1.0.0" },
},
apis: ["./server.js"], // lit les commentaires JSDoc
});
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// Ouvrez http://localhost:3000/docs
Étape 15 — Checklist
✓ Noms de ressources au pluriel
✓ Verbes HTTP cohérents
✓ Codes retour standards (201, 204, 404, 422)
✓ Validation Zod sur toutes les entrées
✓ CORS configuré correctement
✓ Rate limiting en place
✓ Authentification Bearer token
✓ Idempotency-Key sur opérations critiques
✓ Retries + backoff côté client
✓ Tests Supertest pour chaque endpoint
✓ Documentation Swagger/OpenAPI exposée