Construire un projet Fastify
Fastify est un framework web pour Node.js axé sur la performance et l'expérience développeur. Il offre un système de plugins puissant, une validation des schémas intégrée via JSON Schema, et un support natif de TypeScript.
Initialisation d'un projet
mkdir my-app && cd my-app
pnpm init
pnpm add fastify
pnpm add -D typescript @types/node tsxCréer un fichier tsconfig.json :
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"]
}Structure du projet
├── src
│ ├── index.ts # Point d'entrée
│ ├── app.ts # Configuration Fastify
│ ├── plugins
│ │ ├── cors.ts # Plugin CORS
│ │ └── swagger.ts # Plugin OpenAPI
│ ├── routes
│ │ ├── index.ts # Enregistrement des routes
│ │ └── cats
│ │ ├── index.ts # Routes /cats
│ │ ├── cats.schema.ts
│ │ └── cats.service.ts
│ └── utils
│ └── logger.ts
├── test
│ └── routes
│ └── cats.test.ts
├── package.json
└── tsconfig.jsonConventions
Plugins
Fastify repose sur un système de plugins pour structurer l'application. Chaque plugin encapsule un domaine fonctionnel.
import { FastifyPluginAsync } from 'fastify'
import fp from 'fastify-plugin'
const myPlugin: FastifyPluginAsync = async (fastify) => {
fastify.decorate('myUtil', () => {
// ...
})
}
// fp() permet de rendre le plugin accessible dans tout le scope parent
export default fp(myPlugin, {
name: 'my-plugin',
})Validation avec JSON Schema
La validation des entrées est obligatoire et se fait via les JSON Schemas intégrés à Fastify. Il est recommandé d'utiliser @sinclair/typebox pour écrire les schémas avec l'inférence TypeScript :
import { Type, Static } from '@sinclair/typebox'
const CatSchema = Type.Object({
id: Type.Number(),
name: Type.String({ minLength: 1 }),
age: Type.Number({ minimum: 0 }),
})
type Cat = Static<typeof CatSchema>
const CatParams = Type.Object({
id: Type.Number(),
})
export const getCatOpts = {
schema: {
params: CatParams,
response: {
200: CatSchema,
},
},
}Routes
Les routes utilisent le pattern « autoload » via @fastify/autoload ou sont enregistrées manuellement en tant que plugins :
import { FastifyPluginAsync } from 'fastify'
const catsRoutes: FastifyPluginAsync = async (fastify) => {
fastify.get('/', async () => {
return fastify.catsService.findAll()
})
fastify.get<{ Params: { id: number } }>('/:id', { schema: getCatOpts.schema }, async (request) => {
return fastify.catsService.findById(request.params.id)
})
fastify.post<{ Body: Cat }>('/', async (request, reply) => {
const cat = await fastify.catsService.create(request.body)
return reply.code(201).send(cat)
})
}
export default catsRoutesLogging
Fastify intègre pino comme logger par défaut. Il suffit de l'activer à l'initialisation :
import Fastify from 'fastify'
const fastify = Fastify({
logger: {
level: process.env.LOG_LEVEL ?? 'info',
// En développement, utiliser pino-pretty pour un affichage lisible
...(process.env.NODE_ENV === 'development' && {
transport: {
target: 'pino-pretty',
},
}),
},
})Utiliser le logger dans les routes :
fastify.get('/cats', async (request) => {
request.log.info('Récupération de tous les cats')
return catsService.findAll()
})Gestion des erreurs
Définir un gestionnaire d'erreurs global :
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify'
fastify.setErrorHandler((error: FastifyError, request: FastifyRequest, reply: FastifyReply) => {
request.log.error(error)
const statusCode = error.statusCode ?? 500
reply.status(statusCode).send({
statusCode,
error: error.name,
message: error.message,
timestamp: new Date().toISOString(),
})
})Documentation OpenAPI
L'API doit fournir une documentation OpenAPI. Utiliser @fastify/swagger avec @fastify/swagger-ui :
pnpm add @fastify/swagger @fastify/swagger-uiimport fastifySwagger from '@fastify/swagger'
import fastifySwaggerUI from '@fastify/swagger-ui'
await fastify.register(fastifySwagger, {
openapi: {
info: {
title: 'Mon API',
version: '1.0.0',
},
},
})
await fastify.register(fastifySwaggerUI, {
routePrefix: '/documentation',
})Tests
Fastify fournit une méthode inject() pour tester les routes sans démarrer le serveur :
import { describe, it, expect } from 'vitest'
import { buildApp } from '../src/app'
describe('GET /cats', () => {
it('should return a list of cats', async () => {
const app = await buildApp()
const response = await app.inject({
method: 'GET',
url: '/cats',
})
expect(response.statusCode).toBe(200)
expect(response.json()).toBeInstanceOf(Array)
})
})Voir aussi
- Prisma (ORM) — Intégration de l'ORM recommandé
- Architecture des dossiers — Structure de référence Fastify
- Du POC à la production — Checklist avant mise en production
