Skip to content
Learni
Voir tous les tutoriels
Backend

Comment maîtriser Fastify pour des APIs ultra-performantes en 2026

Read in English

Introduction

Fastify est le framework Node.js le plus performant en 2026, surpassant Express de 2 à 3 fois en throughput grâce à son architecture zéro-dépendance et son système de plugins asynchrones. Idéal pour les APIs scalables sous haute charge, il excelle dans la validation de schémas JSON native (via Ajv), les hooks pour l'interception fine des requêtes, et l'écosystème de plugins officiels comme @fastify/jwt ou @fastify/rate-limit.

Ce tutoriel avancé vous guide pas à pas pour construire une API complète : authentification JWT, rate limiting, logging structuré, gestion d'erreurs custom, et clustering pour la production. Chaque étape inclut du code TypeScript fonctionnel, prêt à copier-coller. À la fin, vous maîtriserez les patterns experts pour des APIs qui gèrent des millions de requêtes/jour sans transpirer. Pourquoi Fastify ? Parce qu'il réduit le JSON parsing de 80% et supporte nativement TypeScript, rendant vos déploiements plus fiables et rapides. (148 mots)

Prérequis

  • Node.js 20+ (avec npm ou yarn)
  • Connaissances avancées en TypeScript et Node.js
  • Un éditeur comme VS Code avec extension TypeScript
  • Outils de test : Tap (inclus dans Fastify) ou Jest
  • Base de données optionnelle (ex: PostgreSQL pour démo)

Installation et configuration initiale

terminal
mkdir fastify-advanced-api && cd fastify-advanced-api
npm init -y
npm install fastify@latest @fastify/rate-limit @fastify/jwt @fastify/swagger @fastify/cors @fastify/autoload
npm install -D @types/node typescript tsx
npm install @fastify/merge-json-schemas

echo '{
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/server.ts",
    "start": "tsx src/server.ts"
  }
}' > package.json

mkdir src
mkdir src/plugins
mkdir src/routes
mkdir src/schemas

Cette commande initialise un projet ESM moderne avec les plugins essentiels : rate-limit pour la protection DDoS, JWT pour l'auth, Swagger pour la doc auto, CORS pour les frontends. tsx permet l'exécution directe de TypeScript sans build. Les dossiers structurent le projet en suivant les best practices Fastify (autoload des routes/plugins).

Structure du projet Fastify

Fastify encourage une architecture modulaire : src/server.ts comme point d'entrée, plugins/ pour les services globaux (ex: logger, auth), routes/ pour les handlers, schemas/ pour la validation JSON. Utilisez register pour composer l'app comme des Lego, évitant le callback hell.

Serveur de base avec autoload et CORS

src/server.ts
import Fastify from 'fastify';
import cors from '@fastify/cors';
import autoload from '@fastify/autoload';
import path from 'path';

const fastify = Fastify({
  logger: {
    level: 'info',
    transport: {
      target: 'pino-pretty',
      options: { colorize: true }
    }
  }
});

await fastify.register(cors, { origin: '*' });
await fastify.register(autoload, {
  dir: path.join(__dirname, 'plugins'),
  options: Object.assign({ prefix: '/api' })
});
await fastify.register(autoload, {
  dir: path.join(__dirname, 'routes'),
  options: Object.assign({ prefix: '/api' })
});

fastify.get('/health', async (request, reply) => {
  return { status: 'OK', timestamp: new Date().toISOString() };
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000, host: '0.0.0.0' });
    fastify.log.info(`Serveur démarré sur http://localhost:3000`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

Ce serveur active le logger Pino (le plus rapide), CORS pour les appels cross-origin, et autoload pour charger auto tous les plugins/routes dans /plugins et /routes. Le hook listen gère les erreurs de démarrage. Testez avec npm run dev : curl http://localhost:3000/health retourne un JSON valide.

Plugin de rate limiting global

src/plugins/rateLimit.ts
import rateLimit from '@fastify/rate-limit';

export default async function (fastify: any) {
  await fastify.register(rateLimit, {
    max: 100,
    timeWindow: '1 minute',
    keyGenerator: (request: any) => {
      return request.ip || request.headers['x-forwarded-for'] || 'unknown';
    },
    skipSuccessfulRequests: false,
    onExceeding: async (request: any, reply: any) => {
      reply.code(429).send({
        error: 'Trop de requêtes',
        retryAfter: 60
      });
    }
  });

  fastify.addHook('preHandler', async (request: any, reply: any) => {
    await fastify.rateLimit(request, reply);
  });
}

Ce plugin applique un rate limit de 100 req/min par IP, avec hook preHandler pour l'exécution avant chaque route. keyGenerator utilise l'IP réelle derrière les proxies. Sur dépassement, renvoie un 429 custom. Idéal contre les abus sans impacter les perfs.

Validation de schémas JSON avancée

Analogie : Les schémas Fastify sont comme des contrats JSON Schema : ils valident inputs/outputs automatiquement, rejettent les mauvais payloads avant le handler (économie CPU). Ajoutez $merge pour composer des schémas réutilisables.

Schémas réutilisables et routes validées

src/routes/users.ts
import { FastifyInstance } from 'fastify';
import { createUserSchema, getUserSchema } from '../schemas/user.js';

export async function usersRoutes(fastify: FastifyInstance) {
  fastify.post('/users', { schema: createUserSchema }, async (request, reply) => {
    const { email, password, name } = request.body as any;
    // Simule DB insert
    const userId = 'uuid-' + Date.now();
    reply.code(201).send({ id: userId, email });
  });

  fastify.get('/users/:id', { schema: getUserSchema }, async (request) => {
    const { id } = request.params as any;
    return { id, email: 'user@example.com', name: 'John Doe' };
  });
}

Les schémas valident body/params/response automatiquement (400/500 si invalide). format: 'email' et minLength sont des validators Ajv puissants. La route /api/users est protégée : testez avec curl -X POST ... un mauvais email → erreur 400 structurée.

Plugin JWT avec hooks d'auth

src/plugins/auth.ts
import jwt from '@fastify/jwt';

export default async function (fastify: any) {
  await fastify.register(jwt, {
    secret: 'supersecret2026',
    sign: { expiresIn: '1h' }
  });

  fastify.decorate('authenticate', async function (request: any, reply: any) {
    try {
      await request.jwtVerify();
    } catch (err) {
      reply.send(err);
    }
  });

  fastify.addHook('preValidation', async (request: any, reply: any) => {
    if (request.url.startsWith('/api/protected')) {
      await request.jwtVerify();
    }
  });
}

Ce plugin ajoute jwtVerify() global et un décorateur réutilisable authenticate. Hook preValidation protège auto les routes /protected. Générez un token via une route login (à ajouter), puis utilisez Authorization: Bearer . Évite les boilerplates dans chaque handler.

Hooks avancés et logging

Les hooks Fastify (onRequest, preHandler, preValidation, postHandler) interceptent le cycle de vie comme des middleware mais asynchrones et parallélisables. Couplez-les au logger Pino pour des traces structurées : { reqId, level, msg }.

Gestion d'erreurs custom et Swagger

src/plugins/swagger.ts
import swagger from '@fastify/swagger';
import swaggerUI from '@fastify/swagger-ui';

export default async function (fastify: any) {
  await fastify.register(swagger, {
    openapi: {
      info: {
        title: 'Fastify Advanced API',
        version: '1.0.0'
      }
    }
  });
  await fastify.register(swaggerUI, {
    routePrefix: '/docs'
  });
};

fastify.setErrorHandler((error: any, request: any, reply: any) => {
  const statusCode = error.statusCode || 500;
  fastify.log.error({ err: error, reqId: request.id }, 'Erreur');
  reply.code(statusCode).send({
    error: error.message,
    timestamp: new Date().toISOString()
  });
});

Swagger génère une doc interactive à /docs. L'ErrorHandler global capture toutes les erreurs, logge avec request.id (auto-généré), et renvoie un JSON propre. Ajoutez-le dans server.ts après les registers. Testez : forcez une erreur → log structuré + réponse 500.

Clustering pour production

src/server.prod.ts
import Fastify from 'fastify';
import cluster from 'cluster';
import os from 'os';

const numCPUs = os.cpus().length;

if (cluster.isPrimary) {
  console.log(`Master démarré, fork ${numCPUs} workers`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} mort`);
    cluster.fork();
  });
} else {
  // Collez ici le code de server.ts (imports, fastify instance, listen)
  const fastify = Fastify({ logger: true });
  // ... tous les registers ...
  await fastify.listen({ port: 3000, host: '0.0.0.0' });
}

Utilise le module cluster natif Node pour scaler sur tous les CPU cores. Le primary fork les workers ; exit handler redémarre les crashés (zero-downtime). En prod, booste le throughput x4-8. Lancez avec tsx src/server.prod.ts.

Bonnes pratiques

  • Plugins avant routes : Toujours register(plugins) puis autoload(routes) pour l'ordre d'exec.
  • Schémas par route : Validez response pour des contrats API stricts (OpenAPI compliant).
  • Hooks légers : Évitez les DB calls dans onRequest ; réservez preHandler pour l'auth.
  • Logger structuré : Utilisez request.log partout pour corréler req/err.
  • Tests unitaires : Injectez Fastify dans fastify.inject() pour des tests sans serveur.

Erreurs courantes à éviter

  • Oublier await sur register() : provoque des routes non-chargées silencieusement.
  • Ignorer les conflits de schémas : utilisez @fastify/merge-json-schemas pour $merge.
  • Rate limit sans keyGenerator custom : échoue derrière Cloudflare/NGINX.
  • Pas de clustering en prod : gaspille 80% des CPU multi-cores.

Pour aller plus loin

  • Plugins avancés : @fastify/postgres pour DB poolée, @fastify/redis pour cache.
  • Benchmark : autocannon -c 100 -d 20 http://localhost:3000/api/health.
  • Déploiement : Docker + PM2 ou fastify-cluster.
Découvrez nos formations Node.js avancées pour maîtriser Fastify en profondeur.