Retour au blog

Créer une API REST avec Node.js et TypeScript : guide complet

10 mars 202615 min

Introduction

Créer une API REST robuste et maintenable est essentiel pour tout projet moderne. Dans ce guide, nous allons construire une API complète avec Node.js, TypeScript, Express et les meilleures pratiques de l'industrie.

Architecture du projet

Une bonne architecture facilite la maintenance et l'évolution du code. Voici la structure recommandée :

src/
├── config/          # Configuration (env, database)
├── controllers/     # Logique de requête/réponse
├── middlewares/      # Auth, validation, error handling
├── models/          # Modèles de données (Prisma/TypeORM)
├── routes/          # Définition des routes
├── services/        # Logique métier
├── utils/           # Utilitaires
├── validators/      # Schémas de validation (Zod)
└── app.ts           # Point d'entrée

Séparation des responsabilités

  • Controllers : reçoivent les requêtes, appellent les services, retournent les réponses
  • Services : contiennent la logique métier, indépendants du framework HTTP
  • Validators : valident les données entrantes avec des schémas Zod

Setup initial

Installation des dépendances

mkdir api-project && cd api-project
npm init -y
npm install express cors helmet dotenv
npm install -D typescript @types/express @types/node @types/cors tsx
npx tsc --init

Configuration TypeScript

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"]
}

Point d'entrée

// src/app.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import { router } from './routes';
import { errorHandler } from './middlewares/errorHandler';

const app = express();

app.use(helmet()); app.use(cors()); app.use(express.json()); app.use('/api', router); app.use(errorHandler);

const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(Server running on port ${PORT}); });

Validation avec Zod

Zod est la bibliothèque de validation de référence en TypeScript. Elle permet de définir des schémas typés qui servent à la fois de validation runtime et de types TypeScript.

// src/validators/userValidator.ts
import { z } from 'zod';

export const createUserSchema = z.object({ email: z.string().email('Email invalide'), password: z.string().min(8, 'Minimum 8 caractères'), name: z.string().min(2, 'Minimum 2 caractères'), });

export type CreateUserInput = z.infer;

Middleware de validation

// src/middlewares/validate.ts
import { Request, Response, NextFunction } from 'express';
import { ZodSchema } from 'zod';

export function validate(schema: ZodSchema) { return (req: Request, res: Response, next: NextFunction) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(400).json({ error: 'Validation échouée', details: result.error.flatten().fieldErrors, }); } req.body = result.data; next(); }; }

Authentification JWT

L'authentification par JSON Web Tokens est le standard pour les APIs REST. Voici une implémentation sécurisée :

// src/services/authService.ts
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';

const JWT_SECRET = process.env.JWT_SECRET!; const JWT_EXPIRES_IN = '24h';

export async function hashPassword(password: string): Promise { return bcrypt.hash(password, 12); }

export async function comparePassword( password: string, hash: string ): Promise { return bcrypt.compare(password, hash); }

export function generateToken(userId: string): string { return jwt.sign({ userId }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }); }

export function verifyToken(token: string) { return jwt.verify(token, JWT_SECRET); }

Middleware d'authentification

// src/middlewares/auth.ts
import { Request, Response, NextFunction } from 'express';
import { verifyToken } from '../services/authService';

export function authenticate(req: Request, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'Token manquant' }); }

try { const token = authHeader.split(' ')[1]; const payload = verifyToken(token); req.userId = (payload as { userId: string }).userId; next(); } catch { return res.status(401).json({ error: 'Token invalide' }); } }

Gestion d'erreurs centralisée

Un middleware d'erreur global simplifie la gestion des erreurs dans toute l'application :

// src/middlewares/errorHandler.ts
import { Request, Response, NextFunction } from 'express';

class AppError extends Error { constructor(public statusCode: number, message: string) { super(message); } }

export function errorHandler( err: Error, _req: Request, res: Response, _next: NextFunction ) { if (err instanceof AppError) { return res.status(err.statusCode).json({ error: err.message }); }

console.error('Unexpected error:', err); res.status(500).json({ error: 'Erreur interne du serveur' }); }

export { AppError };

Tests unitaires

Les tests sont essentiels pour garantir la fiabilité de votre API. Utilisez Vitest pour des tests rapides avec un excellent support TypeScript :

// src/services/__tests__/authService.test.ts
import { describe, it, expect } from 'vitest';
import { hashPassword, comparePassword, generateToken } from '../authService';

describe('AuthService', () => { it('should hash and verify password', async () => { const password = 'MonMotDePasse123'; const hash = await hashPassword(password);

expect(hash).not.toBe(password); expect(await comparePassword(password, hash)).toBe(true); expect(await comparePassword('wrong', hash)).toBe(false); });

it('should generate a valid JWT token', () => { const token = generateToken('user-123'); expect(token).toBeDefined(); expect(typeof token).toBe('string'); }); });

Conclusion

Ce guide couvre les fondamentaux d'une API REST professionnelle avec Node.js et TypeScript. Les bonnes pratiques présentées — architecture en couches, validation Zod, authentification JWT, gestion d'erreurs centralisée et tests — constituent une base solide pour n'importe quel projet backend.

En tant que développeur full-stack freelance à Paris, je conçois des APIs robustes et performantes pour mes clients. N'hésitez pas à me contacter pour vos projets.

← Tous les articlesAccueil