🔌 Les API REST avec PHP

Créez des API REST performantes et professionnelles pour vos applications web

⏱️ 3-4 heures de lecture 📚 Niveau intermédiaire 💻 PHP & SQL requis

Qu'est-ce qu'une API REST ?

💡

Définition

API REST (Representational State Transfer) est un style d'architecture pour créer des services web qui permet aux applications de communiquer entre elles via le protocole HTTP.

Imaginez que vous avez créé une application web qui gère une bibliothèque. Vous voulez maintenant créer une application mobile qui utilise les mêmes données. Au lieu de recréer toute la logique, vous créez une API REST qui agit comme un pont entre votre base de données et n'importe quelle application.

Architecture Client-Serveur

Client (Web, Mobile) 💻 API REST (Serveur PHP) 🔌 Base de données (SQL) 🗄️ HTTP Request JSON Response SQL

Pourquoi utiliser une API REST ?

  • Séparation des préoccupations : Le frontend et le backend sont indépendants
  • Réutilisabilité : Une même API peut servir une app web, mobile, IoT, etc.
  • Scalabilité : Plus facile de faire évoluer l'application
  • Standardisation : Utilise les conventions HTTP que tout le monde connaît

Les 6 principes REST

1

Client-Serveur

Séparation claire entre le client (interface) et le serveur (logique métier)

2

Sans état (Stateless)

Chaque requête contient toutes les informations nécessaires. Le serveur ne conserve pas l'état du client.

3

Cacheable

Les réponses peuvent être mises en cache pour améliorer les performances

4

Interface uniforme

Utilisation cohérente des méthodes HTTP et des URLs

5

Système en couches

Architecture modulaire avec différentes couches (routeur, contrôleur, modèle)

6

Code à la demande

Optionnel : Le serveur peut envoyer du code exécutable (JavaScript)

Les méthodes HTTP (Verbes)

Les API REST utilisent les méthodes HTTP pour définir le type d'action à effectuer :

GET 📥

Récupérer des données

Lecture d'une ou plusieurs ressources. Ne modifie pas les données.

GET /api/livre → Tous les livres
GET /api/livre/5 → Livre avec ID 5
POST

Créer une ressource

Ajout d'une nouvelle ressource dans la base de données.

POST /api/livre → Créer un nouveau livre
PUT ✏️

Modifier complètement

Remplacement complet d'une ressource existante.

PUT /api/livre/5 → Modifier le livre 5 (complet)
PATCH 🔧

Modifier partiellement

Modification partielle d'une ressource existante.

PATCH /api/livre/5 → Modifier certains champs du livre 5
DELETE 🗑️

Supprimer une ressource

Suppression d'une ressource existante.

DELETE /api/livre/5 → Supprimer le livre 5
⚠️

Convention CRUD

Les méthodes HTTP correspondent aux opérations CRUD :

  • Create → POST
  • Read → GET
  • Update → PUT / PATCH
  • Delete → DELETE

Query String vs Path Parameters

Il existe deux façons principales de passer des données dans une URL :

🛤️ Path Parameters (Paramètres de chemin)

Les données font partie du chemin de l'URL

Exemple
GET /api/livre/5
GET /api/auteur/Jean-Dupont/livre
GET /api/categorie/fiction/livre/nouveautes

Utilisation

  • Identifier une ressource spécifique
  • Navigation hiérarchique
  • Ressources imbriquées
Exemple concret :
/api/utilisateur/123/commandes/456
→ Commande 456 de l'utilisateur 123

🔍 Query String (Chaîne de requête)

Les données sont ajoutées après le symbole ?

Exemple
GET /api/livre?page=2&limit=10
GET /api/livre?auteur=Dupont&annee=2024
GET /api/livre?sort=titre&order=asc

Utilisation

  • Filtrer les résultats
  • Pagination
  • Tri et recherche
  • Options facultatives
Exemple concret :
/api/produit?categorie=electronique&prix_max=500
→ Produits électroniques de moins de 500$

Syntaxe Query String

/api/ressource?param1=valeur1¶m2=valeur2¶m3=valeur3
  • ? : Début de la query string
  • = : Séparation nom/valeur
  • & : Séparation entre paramètres

Quand utiliser l'un ou l'autre ?

Critère Path Parameters Query String
Obligatoire ✅ Oui, fait partie de la route ❌ Non, paramètres optionnels
Identification ✅ Identifier UNE ressource ❌ Filtrer PLUSIEURS ressources
Hiérarchie ✅ Relations parent/enfant ❌ Pas de hiérarchie
Lisibilité ✅ URLs plus propres ⚠️ Peut devenir long

Routes et Endpoints

📍

Endpoint

Un endpoint est une URL spécifique qui expose une fonctionnalité de l'API. C'est le point d'entrée pour accéder à une ressource.

Conventions de nommage

Utilisez des noms au pluriel /api/livre plutôt que /api/livre
Utilisez des tirets pour les mots composés /api/nouveau-livre plutôt que /api/nouveauLivre
Utilisez des noms, pas des verbes GET /api/livre plutôt que GET /api/getLivre
Organisez hiérarchiquement /api/auteur/5/livre pour les livres de l'auteur 5

Exemple complet : API de bibliothèque

Méthode Endpoint Description
GET /api/livre ivre ivre Liste tous les livres
GET /api/livre/{id} Détails d'un livre spécifique
POST /api/livre Créer un nouveau livre
PUT /api/livre/{id} Modifier complètement un livre
DELETE /api/livre/{id} Supprimer un livre
GET /api/auteur/{id}/livre Livres d'un auteur spécifique
GET /api/categorie/{nom}/livre Livres d'une catégorie

Travailler avec JSON en PHP

Avant de créer notre router, il est essentiel de comprendre comment PHP communique en JSON et comment configurer les en-têtes HTTP correctement.

Le header Content-Type

Le header Content-Type indique au client (navigateur, application mobile, etc.) quel type de données le serveur envoie. Pour une API REST, on utilise toujours application/json.

Configuration des headers
<?php
// ⚠️ IMPORTANT : À mettre au tout début de votre fichier PHP

// 1. Définir le type de contenu en JSON
header('Content-Type: application/json; charset=utf-8');

// 2. Autoriser les requêtes cross-origin (CORS)
header('Access-Control-Allow-Origin: *');

// 3. Autoriser les méthodes HTTP
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

// 4. Autoriser certains headers personnalisés
header('Access-Control-Allow-Headers: Content-Type, Authorization');
?>
⚠️

Attention !

Les headers doivent être définis avant tout output (echo, print_r, etc.). Si vous affichez quelque chose avant, PHP ne pourra plus modifier les headers.

json_encode() : Convertir PHP → JSON

La fonction json_encode() transforme un tableau ou un objet PHP en chaîne JSON.

Exemples de json_encode()
<?php
// Tableau associatif → Objet JSON
$livre = [
    'id' => 1,
    'titre' => 'Le Petit Prince',
    'auteur' => 'Antoine de Saint-Exupéry',
    'annee' => 1943,
    'disponible' => true
];

echo json_encode($livre);
// Résultat : {"id":1,"titre":"Le Petit Prince","auteur":"Antoine de Saint-Exupéry","annee":1943,"disponible":true}

// Tableau indexé → Tableau JSON
$categories = ['Fiction', 'Science-Fiction', 'Philosophie'];
echo json_encode($categories);
// Résultat : ["Fiction","Science-Fiction","Philosophie"]

// Tableau de tableaux → Tableau d'objets JSON
$livres = [
    ['id' => 1, 'titre' => '1984'],
    ['id' => 2, 'titre' => 'Brave New World']
];
echo json_encode($livres);
// Résultat : [{"id":1,"titre":"1984"},{"id":2,"titre":"Brave New World"}]

// Options utiles
echo json_encode($livre, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// JSON_UNESCAPED_UNICODE : Préserve les accents
// JSON_PRETTY_PRINT : Formate le JSON avec indentation
?>

Options courantes de json_encode()

Option Description
JSON_PRETTY_PRINT Formate le JSON avec indentation (utile pour debug)
JSON_UNESCAPED_UNICODE Garde les caractères Unicode (accents, émojis) tels quels
JSON_UNESCAPED_SLASHES Ne pas échapper les slashes dans les URLs
JSON_NUMERIC_CHECK Convertit les chaînes numériques en nombres

Créer un Router en PHP

Un router est le composant qui analyse l'URL et la méthode HTTP pour déterminer quelle action exécuter.

Structure de base

📁 index.php
<?php
// Configuration
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

// Récupérer la méthode HTTP
$method = $_SERVER['REQUEST_METHOD'];

// Récupérer l'URI et nettoyer
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = trim($uri, '/');

// Diviser l'URI en segments
$segments = explode('/', $uri);

// Router simple
if ($segments[0] === 'api' && $segments[1] === 'livres') {
    
    if ($method === 'GET' && !isset($segments[2])) {
        // GET /api/livre - Liste tous les livres
        getLivres();
        
    } elseif ($method === 'GET' && isset($segments[2])) {
        // GET /api/livre/{id} - Un livre spécifique
        getLivre($segments[2]);
        
    } elseif ($method === 'POST') {
        // POST /api/livre - Créer un livre
        createLivre();
        
    } elseif ($method === 'PUT' && isset($segments[2])) {
        // PUT /api/livre/{id} - Modifier un livre
        updateLivre($segments[2]);
        
    } elseif ($method === 'DELETE' && isset($segments[2])) {
        // DELETE /api/livre/{id} - Supprimer un livre
        deleteLivre($segments[2]);
        
    } else {
        http_response_code(404);
        echo json_encode(['error' => 'Route non trouvée']);
    }
    
} else {
    http_response_code(404);
    echo json_encode(['error' => 'Endpoint non trouvé']);
}
?>

Exemple avec .htaccess

Pour avoir des URLs propres, créez un fichier .htaccess :

📁 .htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]

Que fait ce .htaccess ?

  • Redirige toutes les requêtes vers index.php
  • Sauf si le fichier ou dossier existe réellement
  • Préserve les paramètres de query string (QSA)
⚠️

Serveur PHP intégré (php -S)

Le serveur web interne de PHP ne supporte pas les fichiers .htaccess. Pour contourner ce problème, vous devez spécifier un fichier de routage :

# Au lieu de :
php -S localhost:8000

# Utilisez :
php -S localhost:8000 index.php

Ou créez un fichier router.php séparé :

📁 router.php
<?php
// Fichier de routage pour le serveur PHP interne
if (php_sapi_name() === 'cli-server') {
    // Si c'est un fichier réel, le servir tel quel
    $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    $file = __DIR__ . $path;
    
    if (is_file($file)) {
        return false; // Servir le fichier statique
    }
}

// Sinon, rediriger vers index.php
require __DIR__ . '/index.php';
# Démarrer le serveur avec le router :
php -S localhost:8000 router.php

Cette approche permet de conserver les fichiers statiques (CSS, JS, images) tout en redirigeant les requêtes API vers index.php.

Router orienté objet (POO)

📁 Router.php
<?php
class Router {
    private $routes = [];
    
    public function get($path, $callback) {
        $this->addRoute('GET', $path, $callback);
    }
    
    public function post($path, $callback) {
        $this->addRoute('POST', $path, $callback);
    }
    
    public function put($path, $callback) {
        $this->addRoute('PUT', $path, $callback);
    }
    
    public function delete($path, $callback) {
        $this->addRoute('DELETE', $path, $callback);
    }
    
    private function addRoute($method, $path, $callback) {
        $this->routes[] = [
            'method' => $method,
            'path' => $path,
            'callback' => $callback
        ];
    }
    
    public function run() {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        
        foreach ($this->routes as $route) {
            $pattern = preg_replace('/\{[a-zA-Z]+\}/', '([^/]+)', $route['path']);
            $pattern = '#^' . $pattern . '$#';
            
            if ($route['method'] === $method && preg_match($pattern, $uri, $matches)) {
                array_shift($matches); // Enlever le match complet
                call_user_func_array($route['callback'], $matches);
                return;
            }
        }
        
        // Route non trouvée
        http_response_code(404);
        echo json_encode(['error' => 'Route non trouvée']);
    }
}

// Utilisation
$router = new Router();

$router->get('/api/livre', function() {
    // Logique pour récupérer tous les livres
});

$router->get('/api/livre/{id}', function($id) {
    // Logique pour récupérer un livre spécifique
});

$router->post('/api/livre', function() {
    // Logique pour créer un livre
});

$router->run();
?>

Structure des réponses JSON

JSON (JavaScript Object Notation) est le format standard pour les API REST.

Réponse réussie

GET /api/livre/5 200 OK
{
  "id": 5,
  "titre": "Le Petit Prince",
  "auteur": "Antoine de Saint-Exupéry",
  "annee": 1943,
  "isbn": "978-2-07-061275-8",
  "disponible": true,
  "prix": 12.99
}

Liste de ressources

GET /api/livre?page=1&limit=2 200 OK
{
  "data": [
    {
      "id": 1,
      "titre": "1984",
      "auteur": "George Orwell",
      "annee": 1949
    },
    {
      "id": 2,
      "titre": "Le Meilleur des mondes",
      "auteur": "Aldous Huxley",
      "annee": 1932
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 2,
    "total": 150,
    "pages": 75
  }
}

Créer une ressource

POST /api/livre 201 Created
{
  "success": true,
  "message": "Livre créé avec succès",
  "data": {
    "id": 151,
    "titre": "Nouveau livre",
    "auteur": "Auteur Test",
    "created_at": "2026-03-08T10:30:00Z"
  }
}

Codes de statut HTTP

Code Status Description Utilisation
200 OK Succès général GET, PUT, PATCH réussis
201 Created Ressource créée POST réussi
204 No Content Succès sans contenu DELETE réussi
400 Bad Request Requête invalide Données manquantes/invalides
401 Unauthorized Non authentifié Pas de token/session
403 Forbidden Accès refusé Pas les permissions
404 Not Found Ressource introuvable ID n'existe pas
500 Internal Server Error Erreur serveur Bug, erreur PHP/SQL

Gestion des erreurs

Une bonne API doit retourner des messages d'erreur clairs et cohérents.

Format standard d'erreur

Erreur 404 404 Not Found
{
  "error": {
    "code": 404,
    "message": "Livre non trouvé",
    "details": "Aucun livre ne correspond à l'ID 999"
  }
}
Erreur de validation 400 Bad Request
{
  "error": {
    "code": 400,
    "message": "Données de requête invalides",
    "validation_errors": [
      {
        "field": "titre",
        "message": "Le titre est obligatoire"
      },
      {
        "field": "annee",
        "message": "L'année doit être entre 1000 et 2026"
      }
    ]
  }
}

Classe PHP pour gérer les erreurs

📁 ErrorHandler.php
<?php
class ErrorHandler {
    
    public static function notFound($message = "Ressource non trouvée") {
        http_response_code(404);
        echo json_encode([
            'error' => [
                'code' => 404,
                'message' => $message
            ]
        ]);
        exit;
    }
    
    public static function badRequest($message, $validationErrors = []) {
        http_response_code(400);
        $error = [
            'error' => [
                'code' => 400,
                'message' => $message
            ]
        ];
        
        if (!empty($validationErrors)) {
            $error['error']['validation_errors'] = $validationErrors;
        }
        
        echo json_encode($error);
        exit;
    }
    
    public static function unauthorized($message = "Non authentifié") {
        http_response_code(401);
        echo json_encode([
            'error' => [
                'code' => 401,
                'message' => $message
            ]
        ]);
        exit;
    }
    
    public static function serverError($message = "Erreur interne du serveur") {
        http_response_code(500);
        echo json_encode([
            'error' => [
                'code' => 500,
                'message' => $message
            ]
        ]);
        exit;
    }
}

// Utilisation
if (!$livre) {
    ErrorHandler::notFound("Livre avec l'ID $id introuvable");
}
?>

Exemples pratiques complets

1. Endpoint GET - Liste des livres

📁 api/livre.php
<?php
// Connexion à la base de données
$host = 'localhost';
$dbname = 'bibliotheque';
$user = 'root';
$pass = '';

try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Erreur de connexion à la base de données']);
    exit;
}

// Récupérer les paramètres de query string
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
$search = isset($_GET['search']) ? $_GET['search'] : '';

// Calculer l'offset
$offset = ($page - 1) * $limit;

// Construire la requête SQL
$sql = "SELECT * FROM livre WHERE 1=1";
$params = [];

if (!empty($search)) {
    $sql .= " AND (titre LIKE :search OR auteur LIKE :search)";
    $params[':search'] = "%$search%";
}

// Compter le nombre total de résultats
$countSql = str_replace('*', 'COUNT(*) as total', $sql);
$countStmt = $pdo->prepare($countSql);
$countStmt->execute($params);
$total = $countStmt->fetch(PDO::FETCH_ASSOC)['total'];

// Ajouter la pagination
$sql .= " LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);

// Lier les paramètres
foreach ($params as $key => $value) {
    $stmt->bindValue($key, $value);
}
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);

// Exécuter la requête
$stmt->execute();
$livres = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Construire la réponse
$response = [
    'data' => $livres,
    'pagination' => [
        'page' => $page,
        'limit' => $limit,
        'total' => (int)$total,
        'pages' => ceil($total / $limit)
    ]
];

// Retourner la réponse JSON
header('Content-Type: application/json');
echo json_encode($response, JSON_PRETTY_PRINT);
?>

2. Endpoint POST - Créer un livre

📁 create_livre.php
<?php
// Connexion à la base de données (même code que précédemment)
// ...

// Récupérer les données JSON envoyées
$input = file_get_contents('php://input');
$data = json_decode($input, true);

// Validation des données
$errors = [];

if (empty($data['titre'])) {
    $errors[] = ['field' => 'titre', 'message' => 'Le titre est obligatoire'];
}

if (empty($data['auteur'])) {
    $errors[] = ['field' => 'auteur', 'message' => "L'auteur est obligatoire"];
}

if (!isset($data['annee']) || $data['annee'] < 1000 || $data['annee'] > date('Y')) {
    $errors[] = ['field' => 'annee', 'message' => "L'année doit être valide"];
}

// Si erreurs, retourner 400
if (!empty($errors)) {
    http_response_code(400);
    echo json_encode([
        'error' => [
            'code' => 400,
            'message' => 'Données invalides',
            'validation_errors' => $errors
        ]
    ]);
    exit;
}

// Préparer la requête INSERT
$sql = "INSERT INTO livre (titre, auteur, annee, isbn, prix, disponible) 
        VALUES (:titre, :auteur, :annee, :isbn, :prix, :disponible)";

$stmt = $pdo->prepare($sql);

try {
    $stmt->execute([
        ':titre' => $data['titre'],
        ':auteur' => $data['auteur'],
        ':annee' => $data['annee'],
        ':isbn' => $data['isbn'] ?? null,
        ':prix' => $data['prix'] ?? 0,
        ':disponible' => $data['disponible'] ?? true
    ]);
    
    // Récupérer l'ID du livre créé
    $livreId = $pdo->lastInsertId();
    
    // Récupérer le livre complet
    $stmt = $pdo->prepare("SELECT * FROM livre WHERE id = :id");
    $stmt->execute([':id' => $livreId]);
    $livre = $stmt->fetch(PDO::FETCH_ASSOC);
    
    // Retourner 201 Created
    http_response_code(201);
    echo json_encode([
        'success' => true,
        'message' => 'Livre créé avec succès',
        'data' => $livre
    ], JSON_PRETTY_PRINT);
    
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode([
        'error' => [
            'code' => 500,
            'message' => 'Erreur lors de la création du livre'
        ]
    ]);
}
?>

3. Endpoint PUT - Modifier un livre

📁 update_livre.php
<?php
// Récupérer l'ID depuis l'URL
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;

if ($id === 0) {
    http_response_code(400);
    echo json_encode(['error' => 'ID invalide']);
    exit;
}

// Vérifier que le livre existe
$stmt = $pdo->prepare("SELECT * FROM livre WHERE id = :id");
$stmt->execute([':id' => $id]);
$livre = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$livre) {
    http_response_code(404);
    echo json_encode(['error' => 'Livre non trouvé']);
    exit;
}

// Récupérer les nouvelles données
$input = file_get_contents('php://input');
$data = json_decode($input, true);

// Préparer la requête UPDATE
$sql = "UPDATE livre SET 
        titre = :titre,
        auteur = :auteur,
        annee = :annee,
        isbn = :isbn,
        prix = :prix,
        disponible = :disponible
        WHERE id = :id";

$stmt = $pdo->prepare($sql);

try {
    $stmt->execute([
        ':titre' => $data['titre'],
        ':auteur' => $data['auteur'],
        ':annee' => $data['annee'],
        ':isbn' => $data['isbn'] ?? null,
        ':prix' => $data['prix'] ?? 0,
        ':disponible' => $data['disponible'] ?? true,
        ':id' => $id
    ]);
    
    // Récupérer le livre mis à jour
    $stmt = $pdo->prepare("SELECT * FROM livre WHERE id = :id");
    $stmt->execute([':id' => $id]);
    $livre = $stmt->fetch(PDO::FETCH_ASSOC);
    
    http_response_code(200);
    echo json_encode([
        'success' => true,
        'message' => 'Livre modifié avec succès',
        'data' => $livre
    ], JSON_PRETTY_PRINT);
    
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Erreur lors de la modification']);
}
?>

4. Endpoint DELETE - Supprimer un livre

📁 delete_livre.php
<?php
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;

if ($id === 0) {
    http_response_code(400);
    echo json_encode(['error' => 'ID invalide']);
    exit;
}

// Vérifier que le livre existe
$stmt = $pdo->prepare("SELECT * FROM livre WHERE id = :id");
$stmt->execute([':id' => $id]);

if (!$stmt->fetch()) {
    http_response_code(404);
    echo json_encode(['error' => 'Livre non trouvé']);
    exit;
}

// Supprimer le livre
$stmt = $pdo->prepare("DELETE FROM livre WHERE id = :id");

try {
    $stmt->execute([':id' => $id]);
    
    http_response_code(200);
    echo json_encode([
        'success' => true,
        'message' => 'Livre supprimé avec succès'
    ]);
    
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Erreur lors de la suppression']);
}
?>

5. Tester l'API avec JavaScript (Fetch)

📁 test-api.js
// 1. Récupérer tous les livres
async function getAllLivres() {
    try {
        const response = await fetch('/api/livre?page=1&limit=10');
        const data = await response.json();
        console.log('Livres:', data);
    } catch (error) {
        console.error('Erreur:', error);
    }
}

// 2. Récupérer un livre spécifique
async function getLivre(id) {
    try {
        const response = await fetch(`/api/livre/${id}`);
        if (!response.ok) {
            throw new Error('Livre non trouvé');
        }
        const data = await response.json();
        console.log('Livre:', data);
    } catch (error) {
        console.error('Erreur:', error);
    }
}

// 3. Créer un nouveau livre
async function createLivre(livreData) {
    try {
        const response = await fetch('/api/livre', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(livreData)
        });
        
        const data = await response.json();
        
        if (response.ok) {
            console.log('Livre créé:', data);
        } else {
            console.error('Erreur:', data.error);
        }
    } catch (error) {
        console.error('Erreur:', error);
    }
}

// 4. Modifier un livre
async function updateLivre(id, livreData) {
    try {
        const response = await fetch(`/api/livre/${id}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(livreData)
        });
        
        const data = await response.json();
        console.log('Livre modifié:', data);
    } catch (error) {
        console.error('Erreur:', error);
    }
}

// 5. Supprimer un livre
async function deleteLivre(id) {
    try {
        const response = await fetch(`/api/livre/${id}`, {
            method: 'DELETE'
        });
        
        const data = await response.json();
        console.log('Résultat:', data);
    } catch (error) {
        console.error('Erreur:', error);
    }
}

// Exemples d'utilisation
getAllLivres();
getLivre(5);
createLivre({
    titre: 'Nouveau livre',
    auteur: 'Auteur Test',
    annee: 2026,
    prix: 19.99
});
updateLivre(5, { titre: 'Titre modifié' });
deleteLivre(10);

Exercices pratiques

Exercice 1 Facile

Créer une API simple de produits

Objectif : Créer une API REST pour gérer des produits (nom, prix, description)

Étapes :

  1. Créer une table produit avec les champs : id, nom, prix, description
  2. Créer un endpoint GET /api/produit pour lister tous les produits
  3. Créer un endpoint GET /api/produit/{id} pour un produit spécifique
  4. Tester avec un navigateur ou Postman
💡 Indice : Commencez par le code de connexion à la base de données, puis ajoutez la requête SELECT
Exercice 2 Moyen

Ajouter la pagination et le filtrage

Objectif : Améliorer l'API de l'exercice 1 avec pagination et recherche

Critères :

  • Ajouter les paramètres ?page= et ?limit=
  • Ajouter un paramètre ?search= pour filtrer par nom
  • Retourner les infos de pagination dans la réponse JSON
  • Gérer les cas d'erreur (page invalide, etc.)
Exercice 3 Moyen

Implémenter POST, PUT et DELETE

Objectif : Compléter le CRUD complet pour l'API produits

À faire :

  • POST /api/produit : créer un nouveau produit
  • PUT /api/produit/{id} : modifier un produit
  • DELETE /api/produit/{id} : supprimer un produit
  • Valider les données entrantes
  • Retourner les bons codes HTTP (201, 200, 204, 400, 404)
Exercice 4 Difficile

Créer une API avec relations

Objectif : Gérer deux tables liées (categorie et produit)

Structure :

  • Table categorie : id, nom
  • Table produit : id, nom, prix, categorie_id

Endpoints à créer :

  • GET /api/categorie : liste des catégories
  • GET /api/categorie/{id}/produit : produits d'une catégorie
  • GET /api/produit/{id} : produit avec info de catégorie (JOIN)
Projet final Difficile

API de gestion d'événements

Objectif : Créer une API complète pour gérer des événements

Fonctionnalités :

  • CRUD complet sur les événements (titre, date, lieu, description)
  • Filtrer par date (événements futurs/passés)
  • Recherche par mot-clé
  • Pagination
  • Validation complète des données
  • Gestion d'erreurs robuste
  • Documentation de l'API (liste des endpoints)
🌟 Bonus : Créer une page HTML/JS qui consomme l'API pour afficher et gérer les événements

Glossaire

API (Application Programming Interface)

Interface qui permet à des applications de communiquer entre elles. Ensemble de règles et de protocoles.

REST (Representational State Transfer)

Style d'architecture pour créer des services web basés sur HTTP.

Endpoint

URL spécifique qui expose une fonctionnalité de l'API (ex: /api/livre).

JSON (JavaScript Object Notation)

Format de données léger et lisible utilisé pour échanger des informations.

CRUD

Create, Read, Update, Delete - Les 4 opérations de base sur les données.

Query String

Partie de l'URL après le ? contenant des paramètres (ex: ?page=1&limit=10).

Path Parameter

Paramètre intégré dans le chemin de l'URL (ex: /api/livre/{id}).

Stateless (Sans état)

Principe où le serveur ne conserve pas d'informations sur les requêtes précédentes du client.

Router

Composant qui analyse l'URL et dirige la requête vers le bon contrôleur.

HTTP Status Code

Code numérique indiquant le résultat d'une requête (200 = succès, 404 = non trouvé, etc.).

Header HTTP

Métadonnées envoyées avec une requête ou réponse HTTP (Content-Type, Authorization, etc.).

Payload

Données envoyées dans le corps (body) d'une requête HTTP (POST, PUT, PATCH).

Éditeur Markdown

Testez du markdown en temps réel :

✍️ Éditeur

👁️ Aperçu