// Backend

Le CRUD expliqué : Create, Read, Update, Delete

Derrière l’acronyme CRUD se cache l’un des concepts les plus fondamentaux du développement d’applications. Il désigne les quatre opérations de base que l’on peut effectuer sur des données persistantes : Create (créer), Read (lire), Update (modifier) et Delete (supprimer). Que vous construisiez un blog, une boutique ou un réseau social, l’immense majorité du travail consiste à faire du CRUD sur des utilisateurs, des articles, des commandes ou des messages. Ce guide décortique le concept et le relie au HTTP, au SQL et à une API concrète.

Les quatre opérations

Toute application qui manipule des données stockées se ramène, au fond, à ces quatre actions :

  • Create — ajouter une nouvelle donnée. Créer un compte, publier un article, passer une commande.
  • Read — consulter des données existantes. Afficher la liste des produits, ouvrir un profil, lire un message.
  • Update — modifier une donnée existante. Changer un mot de passe, corriger un titre, mettre à jour une adresse.
  • Delete — supprimer une donnée. Fermer un compte, retirer un article, vider un panier.

L’intérêt du CRUD comme grille de lecture est qu’il structure la conception : dès qu’on identifie une ressource dans une application — un utilisateur, un article, une facture — on sait immédiatement quelles opérations prévoir. Cette régularité est ce qui rend le développement d’applications de gestion aussi systématique.

La correspondance avec HTTP

Sur le web, ces quatre opérations se traduisent naturellement en verbes HTTP. C’est le socle de toute API REST, une architecture que détaille notre guide sur les principes d’une API REST. La correspondance est directe :

OpérationVerbe HTTPExemple d’URL
CreatePOSTPOST /articles
ReadGETGET /articles ou GET /articles/42
UpdatePUT ou PATCHPUT /articles/42
DeleteDELETEDELETE /articles/42

Deux subtilités méritent l’attention. Pour la mise à jour, PUT remplace intégralement la ressource — vous envoyez tous les champs — tandis que PATCH ne modifie que les champs fournis. Si vous ne voulez changer que le titre d’un article, PATCH est plus adapté. Par ailleurs, l’URL décrit quelle ressource (/articles/42) et le verbe décrit quoi en faire : on n’écrit jamais /supprimerArticle, on utilise DELETE /articles/42.

La correspondance avec le SQL

Si vos données vivent dans une base relationnelle, le CRUD correspond aussi aux quatre grandes instructions du langage SQL :

OpérationInstruction SQL
CreateINSERT
ReadSELECT
UpdateUPDATE
DeleteDELETE

Concrètement, pour une table articles :

-- Create
INSERT INTO articles (titre, contenu) VALUES ('Le CRUD', 'Un texte...');

-- Read
SELECT * FROM articles WHERE id = 42;

-- Update
UPDATE articles SET titre = 'CRUD expliqué' WHERE id = 42;

-- Delete
DELETE FROM articles WHERE id = 42;

On voit apparaître une chaîne cohérente : une requête POST déclenche un INSERT, une requête GET un SELECT, et ainsi de suite. Une API CRUD n’est finalement qu’un pont entre le monde HTTP (le client) et le monde SQL (la base de données), avec un peu de logique métier au milieu.

Une API CRUD complète

Assemblons tout cela dans une API concrète, écrite avec Express.js. L’exemple gère une collection d’articles et couvre les quatre opérations. Pour la clarté, on suppose disposer d’un objet db capable d’exécuter des requêtes SQL de façon asynchrone :

import express from "express";
const app = express();
app.use(express.json());

// CREATE — POST /articles
app.post("/articles", async (req, res, next) => {
  try {
    const { titre, contenu } = req.body;
    if (!titre) {
      return res.status(400).json({ erreur: "Le titre est obligatoire" });
    }
    const resultat = await db.query(
      "INSERT INTO articles (titre, contenu) VALUES (?, ?)",
      [titre, contenu]
    );
    res.status(201).json({ id: resultat.insertId, titre, contenu });
  } catch (e) { next(e); }
});

// READ (collection) — GET /articles
app.get("/articles", async (req, res, next) => {
  try {
    const articles = await db.query("SELECT * FROM articles");
    res.json(articles);
  } catch (e) { next(e); }
});

// READ (un élément) — GET /articles/:id
app.get("/articles/:id", async (req, res, next) => {
  try {
    const [article] = await db.query(
      "SELECT * FROM articles WHERE id = ?", [req.params.id]
    );
    if (!article) {
      return res.status(404).json({ erreur: "Article introuvable" });
    }
    res.json(article);
  } catch (e) { next(e); }
});

// UPDATE — PATCH /articles/:id
app.patch("/articles/:id", async (req, res, next) => {
  try {
    const { titre, contenu } = req.body;
    await db.query(
      "UPDATE articles SET titre = ?, contenu = ? WHERE id = ?",
      [titre, contenu, req.params.id]
    );
    res.json({ id: Number(req.params.id), titre, contenu });
  } catch (e) { next(e); }
});

// DELETE — DELETE /articles/:id
app.delete("/articles/:id", async (req, res, next) => {
  try {
    await db.query("DELETE FROM articles WHERE id = ?", [req.params.id]);
    res.status(204).end();
  } catch (e) { next(e); }
});

app.listen(3000);

Chaque route incarne une opération CRUD, s’appuie sur le bon verbe HTTP et déclenche l’instruction SQL correspondante. Notez l’usage systématique des requêtes paramétrées — les ? remplacés par les valeurs du tableau — plutôt que la concaténation de chaînes : c’est la protection de référence contre les injections SQL.

Codes de statut : dire la vérité

Une API CRUD soignée renvoie le bon code de statut HTTP à chaque étape. Ce n’est pas un détail : le client s’appuie dessus pour réagir automatiquement.

  • Create réussi : 201 Created, idéalement avec la ressource créée dans le corps.
  • Read réussi : 200 OK, ou 404 Not Found si la ressource n’existe pas.
  • Update réussi : 200 OK avec la ressource mise à jour.
  • Delete réussi : 204 No Content, sans corps de réponse.
  • Données invalides : 400 Bad Request ou 422 Unprocessable Entity.

Renvoyer un 200 accompagné d’un message d’erreur dans le corps est une mauvaise habitude fréquente : elle prive le client de toute réaction automatique fiable.

Bonnes pratiques à retenir

Au-delà de la mécanique, quelques principes distinguent une API CRUD amateur d’une API robuste.

Valider les entrées. Ne faites jamais confiance aux données envoyées par le client. Vérifiez la présence des champs obligatoires, leur type et leur format avant d’écrire en base. Des bibliothèques comme Zod ou Joi automatisent cette validation.

Se protéger des injections. Utilisez toujours des requêtes paramétrées, jamais de concaténation directe de valeurs utilisateur dans une requête SQL. C’est la faille la plus classique et la plus dévastatrice.

Paginer les lectures. Un GET /articles qui renvoie 100 000 lignes est un problème. Prévoyez la pagination via la query string : GET /articles?page=2&taille=20.

Gérer les droits. Toutes les opérations ne sont pas permises à tous. Créer et supprimer exigent souvent une authentification, et modifier suppose de vérifier que l’utilisateur est bien propriétaire de la ressource.

Rester idempotent quand c’est possible. Appeler DELETE /articles/42 deux fois doit aboutir au même état final : l’article est supprimé, la seconde requête ne provoque pas d’erreur bloquante.

En résumé

Le CRUD est la colonne vertébrale de presque toutes les applications de données. Ses quatre opérations — Create, Read, Update, Delete — se traduisent élégamment en verbes HTTP côté API et en instructions SQL côté base. Maîtriser cette correspondance, c’est disposer d’un modèle mental applicable à n’importe quel projet : identifiez vos ressources, déclinez le CRUD pour chacune, soignez les codes de statut et la validation. Le reste n’est que de la variation autour de ce socle.

À lire ensuite