Node.js, JavaScript côté serveur
Node.js permet d’exécuter JavaScript en dehors du navigateur, côté serveur. Cette unification du langage front et back simplifie considérablement l’apprentissage et la maintenance. Associé au framework Express, léger et flexible, Node.js est devenu un choix majeur pour construire des API REST. Ce tutoriel propose une prise en main pratique : créer une API complète pour gérer des ressources, avec base de données, authentification et bonnes pratiques.
Installation et initialisation
Installez Node.js depuis nodejs.org. Version LTS recommandée pour la stabilité. Créez un dossier, initialisez : npm init -y. Cela génère package.json. Installez Express : npm install express. Pour le développement : npm install –save-dev nodemon typescript @types/express @types/node.
Configurez TypeScript : npx tsc –init. Cela crée tsconfig.json. Ajustez pour cibler Node moderne et ES2022.
Structure de projet recommandée
src/ contient le code source. src/index.ts est le point d’entrée. src/routes/ pour les routes. src/controllers/ pour la logique. src/services/ pour le métier. src/models/ pour les données. src/middleware/ pour les intermédiaires. Cette séparation facilite la maintenance.
Premier serveur Express
Dans src/index.ts :
import express from 'express';
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.json({message: 'API opérationnelle'});
});
app.listen(3000, () => {
console.log('Serveur démarré sur http://localhost:3000');
});
Lancez : npx tsx src/index.ts ou configurez un script npm. L’API répond à GET /.
Les routes RESTful
Une API REST suit des conventions : GET /users (liste), GET /users/:id (un utilisateur), POST /users (création), PUT /users/:id (mise à jour complète), PATCH /users/:id (mise à jour partielle), DELETE /users/:id (suppression). Ces verbes HTTP expriment clairement l’intention.
Exemple : app.get(‘/users/:id’, (req, res) => { const id = req.params.id; res.json({id, name: ‘Test’}); });. Le paramètre :id est accessible via req.params.id.
La base de données avec PostgreSQL et Prisma
Prisma est un ORM moderne TypeScript-first qui simplifie l’interaction base de données. Installez : npm install prisma @prisma/client. Initialisez : npx prisma init. Modifiez schema.prisma pour définir les modèles :
model User {
id Int @id @default(autoincrement())
email String @unique
name String
createdAt DateTime @default(now())
}
Générez le client : npx prisma migrate dev. Prisma crée la base, la table, et génère un client typé.
Utiliser Prisma
const prisma = new PrismaClient();. Ensuite : prisma.user.findMany() liste tous, prisma.user.findUnique({where: {id: 1}}) trouve un, prisma.user.create({data: {…}}) crée, prisma.user.update() met à jour, prisma.user.delete() supprime. L’autocomplétion est excellente.
Les middleware
Les middleware interviennent avant les routes : parsing du body (express.json()), logging, authentification, validation, gestion d’erreurs. app.use(myMiddleware) les enregistre globalement ; on peut aussi les appliquer à des routes spécifiques.
Exemple de middleware de logging : app.use((req, res, next) => { console.log(req.method, req.url); next(); });. L’appel à next() passe la main au middleware suivant.
La validation des entrées
Validez systématiquement les données reçues. Zod est une bibliothèque excellente :
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100)
});
app.post('/users', (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) return res.status(400).json(result.error);
// création avec result.data
});
Cette discipline prévient les erreurs et certaines vulnérabilités.
L’authentification avec JWT
Les JWT (JSON Web Tokens) permettent une authentification sans session serveur. À la connexion, signez un token avec les informations utilisateur et une clé secrète. Les requêtes suivantes envoient le token, le serveur le vérifie.
npm install jsonwebtoken bcrypt @types/jsonwebtoken @types/bcrypt. Bcrypt hache les mots de passe avant stockage. JWT génère et vérifie les tokens. Les durées de vie courtes (15 minutes) avec refresh tokens améliorent la sécurité.
Middleware d’authentification
Un middleware vérifie le token sur les routes protégées : extrait le token de l’en-tête Authorization, vérifie avec jwt.verify, attache l’utilisateur à req. Les routes en aval ont accès à l’utilisateur authentifié.
La gestion des erreurs
Un middleware spécial (quatre paramètres : err, req, res, next) centralise la gestion d’erreurs. Définissez des classes d’erreur avec codes HTTP (400, 401, 403, 404, 500). Le middleware traduit ces erreurs en réponses JSON propres.
try/catch dans les handlers async, ou une fonction d’enrobage asyncHandler qui forward les erreurs au middleware.
CORS
Si votre front et votre API sont sur des domaines différents, CORS bloque les requêtes par défaut. Le package cors configure les autorisations : npm install cors. app.use(cors({origin: ‘https://monfront.com’}));. Spécifiez précisément les origines autorisées plutôt que *.
Les variables d’environnement
Ne commitez jamais secrets dans le code. Utilisez dotenv : npm install dotenv. Créez .env avec DATABASE_URL, JWT_SECRET, etc. import ‘dotenv/config’; au début de index.ts charge les variables. Ajoutez .env à .gitignore.
Les logs structurés
console.log suffit en développement, mais en production privilégiez un logger comme Pino ou Winston : logs structurés (JSON), niveaux (info, warn, error), rotation des fichiers, envoi vers des services d’agrégation. Ces logs facilitent l’investigation et la supervision.
Les tests
Vitest ou Jest pour les tests unitaires et d’intégration. Supertest pour tester les routes : il simule des requêtes HTTP sans démarrer de vrai serveur. Les tests garantissent que les évolutions ne cassent pas les comportements existants.
Structurez : tests unitaires pour les services/utils, tests d’intégration pour les flux complets. Viser 70-80 % de couverture est un bon objectif pour une API.
La pagination et le filtrage
Pour les listes, implémentez pagination (limit, offset ou cursor), filtrage (par colonnes), tri (ordre ascendant/descendant). Ces fonctionnalités sont attendues des clients et évitent de rapatrier des volumes inutiles. Documentez les paramètres attendus.
Le rate limiting
Protégez contre les abus avec express-rate-limit : limite le nombre de requêtes par IP sur une fenêtre de temps. Ajuster selon les routes : plus strict sur /login, plus souple sur les lectures publiques. Cette protection basique neutralise les attaques brute force naïves.
La documentation avec OpenAPI
Documentez votre API avec OpenAPI (Swagger). Swagger UI génère automatiquement une interface de documentation interactive. Les outils comme tsoa, zod-to-openapi génèrent la spec depuis le code TypeScript, garantissant qu’elle reste à jour.
Le déploiement
Plusieurs options : Railway, Render, Fly.io (simples, adaptés aux PME), AWS, GCP, Azure (plus complexes, plus de flexibilité), un VPS classique (contrôle total). Docker facilite la portabilité : un Dockerfile permet de déployer la même image partout.
Variables d’environnement de production, HTTPS obligatoire (Let’s Encrypt ou géré par la plateforme), base de données managée recommandée.
La supervision en production
Suivez en production : temps de réponse, taux d’erreur, utilisation CPU/mémoire, erreurs inattendues. Outils : Sentry pour les erreurs, services APM (New Relic, Datadog, ou alternatives open source comme Grafana), logs centralisés.
Conclusion : API solide par les fondamentaux
Node.js et Express constituent une base accessible et puissante pour les API REST. Les bonnes pratiques (structure claire, validation, authentification sécurisée, tests, supervision) transforment un serveur fonctionnel en API professionnelle. Pour un projet de PME, ce stack permet d’aller vite sans sacrifier la qualité. Lancez-vous sur un projet concret : construire une API pour un besoin réel ancre les apprentissages et révèle les subtilités que la théorie ne couvre pas.