Accueil / Articles / Sept exercices de codage pour débutants avec Node.js et Express, résolus et expliqués

Sept exercices de codage pour débutants avec Node.js et Express, résolus et expliqués

Travaillez sur sept exercices typiques d’entretien pour les débutants en Node.js, allant des routes Express et du middleware à la gestion des erreurs, au renversement de chaînes de caractères et aux doublons, et apprenez à expliquer chacun d’eux.

1640 mots

Les entretiens pour des postes junior en Node.js et Express reposent rarement sur des algorithmes complexes. On vous demande généralement d’effectuer des tâches courtes, comme créer une route, écrire un middleware ou nettoyer un tableau, tandis que l’interviewer observe la manière dont vous raisonnez et si vous pouvez expliquer pourquoi votre code fonctionne. Les sept tâches ci-dessous couvrent les aspects les plus courants, chacune accompagnée d’une solution fonctionnelle, d’une explication des lignes clés et de points complémentaires permettant de vérifier une véritable compréhension.

Tâche 1 : Créer une route GET de base

Un exercice classique d’échauffement : « Créez une extrémité GET à / qui renvoie Hello World. » La solution consiste à créer une application Express, à enregistrer un gestionnaire pour le chemin racine et à commencer à écouter sur un port.

import express from "express";

const app = express();

app.get("/", (req, res) => {
    res.status(200).send("Hello World");
});

app.listen(3000, () => {
    console.log("Server running on port 3000");
});

Puisqu’il utilise import, le projet nécessite l’élément "type": "module" dans package.json ou une extension .mjs.

Lecture de la route ligne par ligne

L’appel à app.get() enregistre un gestionnaire qui ne réagit qu’aux requêtes GET :

app.get("/", (req, res) => {

Le premier argument, /, correspond au chemin. La fonction de rappel reçoit req, qui décrit la requête entrante, et res, utilisé pour construire la réponse.

La réponse elle-même est générée ici :

res.status(200).send("Hello World");

status(200) définit le code d’état (200 étant la valeur par défaut, mais être explicite rend le code plus lisible), et send() écrit le corps de la réponse et termine celle-ci.

Finalement, le serveur est démarré :

app.listen(3000, () => {

Cela lie l’application au port 3000 ; la fonction de rappel s’exécute dès que le serveur accepte des connexions. Les services réels lisent généralement le port depuis process.env.PORT.

Comment l’exprimer

"app.get() enregistre une route pour les requêtes GET. Lorsqu’un client appelle /, Express exécute la fonction de rappel, qui répond avec un statut 200 et le texte Hello World."

Tâche 2 : Écrire un middleware de journalisation des requêtes

Prochaine instruction : "Créez un middleware qui enregistre chaque requête entrante." Un middleware est une fonction ayant pour signature (req, res, next). Celui-ci affiche la méthode et l’URL, puis transfère le contrôle ; app.use() l’applique à toutes les routes enregistrées après lui.

const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
};

app.use(logger);

Chaque requête passe dans la chaîne dans l’ordre avant que le gestionnaire de route ne réponde :

Client
  ↓
Request
  ↓
Logger Middleware
  ↓
Route Handler
  ↓
Response

La ligne la plus importante est bien celle qui se trouve en dernier à l’intérieur de la fonction :

next();

next() indique à Express que cette étape est terminée et que le middleware ou la route suivante doit être exécutés. Si l’on oublie cela, la requête reste bloquée jusqu’à ce que le client expire, ce qui est une question fréquente. L’alternative consiste à terminer la requête en envoyant une réponse, comme le fait un middleware d’authentification lorsqu’il rejette un jeton manquant.

Comment le formuler

"Le middleware reçoit la requête, la réponse et next. Il peut examiner ou modifier la requête, puis soit répondre, soit appeler next() pour transférer le contrôle."

Tâche 3 : Gérer le travail asynchrone dans une route

Les points de terminaison réels attendent des bases de données et d’autres API, donc on peut s’attendre à la question « Comment gérer une opération asynchrone dans Express ? ». Une réponse solide consiste en un gestionnaire async avec try/catch:

app.get("/users", async (req, res, next) => {
    try {
        const users = await getUsers();
        res.status(200).json(users);
    } catch (error) {
        next(error);
    }
});

La ligne clé est await:

const users = await getUsers();

Cela pause ce gestionnaire jusqu’à ce que getUsers() soit résolu, sans bloquer les autres requêtes. Si la promesse est rejetée, le bloc catch s’exécute :

catch (error) {
    next(error);
}

next(error) transmet l’échec au middleware de gestion des erreurs du Devoir 5. Il convient d’ajouter que Express 4 ne capture pas les promesses rejetées par les gestionnaires asynchrones, ce qui rend try/catch essentiel, tandis qu’Express 5 les transmet automatiquement. Pour en savoir plus, consultez la création d’un système de gestion des erreurs de niveau production dans les applications Node.js.

Tâche 4 : Esquisser les points d’entrée CRUD

CRUD signifie Créer, Lire, Mettre à jour et Supprimer, les quatre opérations de base sur les données, chacune correspondant à une méthode HTTP. Exemples pour la ressource users :

app.post("/users", (req, res) => {
    res.status(201).send("User created");
});

app.get("/users", (req, res) => {
    res.status(200).send("Users");
});
app.put("/users/:id", (req, res) => {
    res.status(200).send("User updated");
});
app.delete("/users/:id", (req, res) => {
    res.status(200).send("User deleted");
});

La création renvoie 201 Created, et les routes relatives à un utilisateur unique prennent un paramètre :id lu depuis req.params.id. Plutôt que de mémoriser des lignes de code, comprenez ce que signifie chaque méthode :

POST   → Create
GET    → Read
PUT    → Update
DELETE → Delete

Soyez prêt à distinguer PUT de PATCH : PUT remplace traditionnellement toute la ressource, tandis que PATCH applique une modification partielle.

Comment le formuler

"CRUD couvre les quatre opérations de base sur les données. Dans REST, POST crée, GET lit, PUT ou PATCH met à jour, et DELETE supprime."

Tâche 5 : Centraliser la gestion des erreurs

La gestion des erreurs suit souvent la Tâche 3. Commencez par une route qui échoue et redirige l’erreur :

app.get("/", (req, res, next) => {
    try {
        throw new Error("Something went wrong");
    } catch (error) {
        next(error);
    }
});

Ensuite, ajoutez un gestionnaire d’erreurs qui enregistre l’événement et renvoie une erreur générique 500 :

app.use((err, req, res, next) => {
    console.error(err.message);
    res.status(500).json({
        message: "Internal Server Error"
    });
});

Ce qui rend cette fonction particulière, c’est sa signature :

(err, req, res, next)

Express reconnaît les gestionnaires d’erreurs à leurs quatre paramètres déclarés, dans l’ordre err, req, res, next ; ils s’exécutent uniquement lorsque une erreur est transmise. Enregistrez le gestionnaire après vos routes, et évitez d’envoyer err.message aux clients en environnement de production. Le flux est le suivant :

Route
  ↓
Error occurs
  ↓
catch(error)
  ↓
next(error)
  ↓
Error middleware
  ↓
500 Response

Est-ce que next() est nécessaire à l’intérieur du gestionnaire d’erreurs ?

Non, si le gestionnaire envoie lui-même la réponse :

app.use((err, req, res, next) => {
    console.error(err.message);
    res.status(500).send("Internal Server Error");
});

next doit néanmoins être déclaré, car avec trois paramètres Express traite la fonction comme un middleware ordinaire.

Tâche 6 : Inverser une chaîne de caractères

Certaines questions relèvent simplement de JavaScript : « Inversez la chaîne hello. » La solution idiomatique utilise trois méthodes enchaînées :

const str = "hello";
const reversed = str.split("").reverse().join("");
console.log(reversed);

Elle affiche :

olleh

Fonction de chaque étape

La fonction split génère les caractères :

str.split("")

Le résultat est un tableau :

["h", "e", "l", "l", "o"]

Puis l’ordre des éléments est inversé sur place :

.reverse()

Cela donne :

["o", "l", "l", "e", "h"]

Enfin, les caractères sont réunis sans séparateur :

.join("")

Cela produit la chaîne inversée :

"olleh"

Une variante courante : « Faites-le maintenant sans reverse(). » Itérez depuis l’index le plus élevé jusqu’à zéro :

const str = "hello";
let reversed = "";
for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
}
console.log(reversed);

Les deux méthodes sont linéaires. Il convient de noter que split("") et l’indexation fonctionnent sur des unités de code UTF-16, ce qui entraîne la fragmentation des emojis ; [...str] quant à lui effectue une séparation par point de code.

Tâche 7 : Trouver les doublons dans un tableau

Un autre favori : « Retourner les valeurs qui apparaissent plus d’une fois. » Données :

const numbers = [1, 2, 3, 2, 4, 3, 5];

Le résultat attendu est :

[2, 3]

Utilisez deux objets Set, un pour les valeurs déjà vues et un pour les doublons confirmés :

const numbers = [1, 2, 3, 2, 4, 3, 5];
const seen = new Set();
const duplicates = new Set();
for (const number of numbers) {
    if (seen.has(number)) {
        duplicates.add(number);
    } else {
        seen.add(number);
    }
}
console.log([...duplicates]);

L’exécution génère le suivant enregistré :

[2, 3]

Pourquoi deux Sets fonctionnent-ils

seen enregistre chaque valeur transmise. La première occurrence y est ajoutée :

seen.add(number);

Une répétition fait que cette vérification réussit :

if (seen.has(number))

La valeur est alors placée dans duplicates, et comme un Set ignore les répétitions, elle est signalée une seule fois. Les recherches ont un temps quasi constant, ce qui rend la solution linéaire plutôt que quadratique avec des boucles imbriquées. Pour compter les occurrences, utilisez un Map.

Expliquez votre approche avant de coder

L’habitude la plus importante est d’expliquer votre plan avant d’écrire du code, car les interviewers évaluent autant le raisonnement que les résultats. Pour la tâche des doublons, commencez par quelque chose comme ceci :

"Le plan consiste en un ensemble de valeurs déjà vues. Lors du bouclage, toute valeur déjà présente y est ajoutée à un deuxième ensemble de doublons, ce qui permet de conserver chaque valeur unique."

Puis écrivez le code. Cela prouve une compréhension profonde plutôt qu’une simple mémorisation et permet à l’intervieweur de vous orienter dès le début.

Liste de vérification pour la préparation

Au avant d’un entretien pour un poste junior en Node.js ou Express, assurez-vous de pouvoir faire sans difficulté :

  • Définir des routes et expliquer le rôle de req et res
  • Utiliser app.use() et écrire vos propres middleware
  • Expliquer ce que fait next() et ce qui se passe en son absence
  • Gérer le travail asynchrone avec async/await, try/catch et next(error)
  • Rédiger un gestionnaire d’erreurs à quatre paramètres et le déclarer après les routes
  • Associer les opérations CRUD aux méthodes HTTP et aux codes d’état appropriés
  • Manipuler des chaînes de caractères et des tableaux à l’aide des méthodes de base de JavaScript
  • Choisir entre Set et Map pour les recherches et le comptage
  • Décrire votre approche tout en codant
  • À ce niveau, des fondamentaux solides, un code lisible et des explications claires comptent bien plus que des algorithmes avancés.

    Lectures complémentaires

  • Associer les types temporels aux tâches date réelles : sept exemples concrets — Travaillez sur sept tâches quotidiennes liées aux dates et heures à l’aide de JavaScript Temporal, allant des dates simples et du dépassement de mois aux fuseaux horaires, à l’arithmétique des heures d’été et à la validation.
  • Préparation à l’entretien Node.js et Express : 70 questions avec des réponses précises — Une revue structurée de 70 questions d’entretien sur Node.js et Express, couvrant le cycle d’événements et les flux, ainsi que le middleware, la sécurité et l’escalabilité, avec des corrections pour les réponses courantes.