E-NO Logo
EN FR
Express API local lab 9 Min Read

Mise en place d'un Express API local lab avec des exemples pratiques : guide d'implémentation

calendar_today Published: 2026-07-23
update Last Updated: 2026-07-23
analytics SEO Efficiency: 100%
Technical guide illustration for Mise en place d'un Express API local lab avec des exemples pratiques : guide d'implémentation.

Introduction

Un lab local sécurisé vous permet d'essayer des idées, de diagnostiquer des problèmes et d'apprendre rapidement sans risquer la production. Dans ce guide, vous allez :

  • Démarrer un Express API minimal sur localhost
  • Ajouter des réglages de sécurité par défaut (helmet, CORS, rate limiting)
  • Mettre en place un logging structuré avec des identifiants de requête
  • Écrire quelques tests Jest + Supertest
  • Exercer l'API avec curl et une charge légère
  • Optionnellement brancher un MongoDB local

Le résultat est un workflow reproductible et inspectable que vous pourrez étendre selon vos besoins. Ce tutoriel illustre un Express API setup complet, utile pour l'Express API testing et l'Express API development, avec des Express API examples concrets.

Vue d'ensemble du workflow

Suivez la séquence ci-dessous et validez chaque étape avant d'avancer.

  1. Préparer la toolchain
  • Installez Node.js 18+ et Git. Optionnel : Docker pour un MongoDB local.
  • Créez un dossier vierge pour le lab :
mkdir express-lab && cd express-lab
npm init -y
  1. Installer les dépendances
npm install express helmet cors express-rate-limit pino pino-http zod dotenv
npm install --save-dev nodemon jest supertest cross-env
  1. Disposition du projet
express-lab/
  .env               # variables d'environnement (ne jamais committer de secrets)
  package.json
  src/
    index.js         # point d'entrée
    routes.js        # routes d'exemple
    errors.js        # helpers d'erreurs
  test/
    app.test.js      # tests de base
  1. Variables d'environnement

Créez un fichier .env :

PORT=4000
HOST=127.0.0.1
CORS_ORIGIN=http://localhost:3000
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=60
NODE_ENV=development
  1. Scripts dans package.json
{
  "name": "express-lab",
  "version": "1.0.0",
  "type": "commonjs",
  "scripts": {
    "dev": "nodemon src/index.js",
    "start": "node src/index.js",
    "test": "cross-env NODE_ENV=test jest --runInBand --detectOpenHandles"
  },
  "jest": {
    "testEnvironment": "node",
    "verbose": false
  }
}
  1. Code de l'app : sécurité, logs et endpoints

src/errors.js :

class HttpError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}

function notFound(req, res, next) {
  next(new HttpError(404, 'Not found'));
}

function errorHandler(err, req, res, next) { // eslint-disable-line no-unused-vars
  const status = err.status || 500;
  res.status(status).json({
    error: {
      message: err.message || 'Internal server error',
      status
    }
  });
}

module.exports = { HttpError, notFound, errorHandler };

src/routes.js :

const express = require('express');
const { z } = require('zod');
const { HttpError } = require('./errors');

const router = express.Router();

router.get('/health', (req, res) => {
  res.json({ status: 'ok', uptime: process.uptime(), ts: Date.now() });
});

router.post('/echo', (req, res, next) => {
  const schema = z.object({ message: z.string().min(1) });
  const parsed = schema.safeParse(req.body);
  if (!parsed.success) return next(new HttpError(400, 'Invalid body'));
  res.json({ echoed: parsed.data.message });
});

router.get('/math/sum', (req, res, next) => {
  const raw = (req.query.numbers || '').toString();
  if (!raw) return next(new HttpError(400, 'Provide numbers query param'));
  const nums = raw.split(',').map(x => Number(x.trim()));
  if (nums.some(n => Number.isNaN(n))) return next(new HttpError(400, 'numbers must be comma-separated numerics'));
  const sum = nums.reduce((a, b) => a + b, 0);
  res.json({ sum, count: nums.length });
});

module.exports = router;

src/index.js :

require('dotenv').config();
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const pino = require('pino');
const pinoHttp = require('pino-http');
const routes = require('./routes');
const { notFound, errorHandler } = require('./errors');

const app = express();
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });

app.disable('x-powered-by');
app.set('trust proxy', false);

app.use(pinoHttp({
  logger,
  genReqId: (req) => req.headers['x-request-id'] || `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
}));
app.use(helmet());
app.use(cors({ origin: process.env.CORS_ORIGIN || 'http://localhost:3000', credentials: true }));
app.use(express.json({ limit: '100kb' }));

const limiter = rateLimit({
  windowMs: Number(process.env.RATE_LIMIT_WINDOW_MS || 60000),
  max: Number(process.env.RATE_LIMIT_MAX || 60),
  standardHeaders: true,
  legacyHeaders: false
});
app.use(limiter);

app.use('/api', routes);
app.use(notFound);
app.use(errorHandler);

const PORT = Number(process.env.PORT || 4000);
const HOST = process.env.HOST || '127.0.0.1';

app.listen(PORT, HOST, () => {
  logger.info({ PORT, HOST }, 'Server started');
});
  1. Exécuter le lab
npm run dev
# Dans un autre terminal
curl -s http://127.0.0.1:4000/api/health | jq .
curl -s -X POST http://127.0.0.1:4000/api/echo -H 'Content-Type: application/json' -d '{"message":"hello"}' | jq .
curl -s 'http://127.0.0.1:4000/api/math/sum?numbers=1,2,3,4' | jq .
  1. Tests de base

test/app.test.js :

const request = require('supertest');
const express = require('express');
const routes = require('../src/routes');
const { notFound, errorHandler } = require('../src/errors');

function createTestApp() {
  const app = express();
  app.use(express.json());
  app.use('/api', routes);
  app.use(notFound);
  app.use(errorHandler);
  return app;
}

describe('Express lab', () => {
  const app = createTestApp();

  test('GET /health returns ok', async () => {
    const res = await request(app).get('/api/health');
    expect(res.statusCode).toBe(200);
    expect(res.body.status).toBe('ok');
  });

  test('POST /echo requires message', async () => {
    const res = await request(app).post('/api/echo').send({});
    expect(res.statusCode).toBe(400);
  });

  test('GET /math/sum sums numbers', async () => {
    const res = await request(app).get('/api/math/sum').query({ numbers: '5,7,8' });
    expect(res.statusCode).toBe(200);
    expect(res.body.sum).toBe(20);
  });
});

Lancez les tests :

npm test
  1. Charge rapide et vérifications de sécurité
  • Rate limit : déclenchez-la pour confirmer le comportement 429.
for i in $(seq 1 70); do curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:4000/api/health; done | sort | uniq -c
  • Request ID : observez que les logs incluent un identifiant de requête. Ajoutez -H 'x-request-id: demo-123' et confirmez que le même ID apparaît dans les logs.
curl -H 'x-request-id: demo-123' -s http://127.0.0.1:4000/api/health > /dev/null
# Vérifiez les logs serveur pour demo-123
  1. Optionnel : ajouter un MongoDB local

Si vous souhaitez une couche de données réaliste, démarrez MongoDB en local. Une option simple consiste à lancer un conteneur sur localhost.

# Démarrer MongoDB localement (réglages par défaut dev)
docker run -d --name mongo-lab -p 27017:27017 mongo:6

Installez Mongoose et ajoutez une petite fonctionnalité de notes :

npm install mongoose

src/db.js :

const mongoose = require('mongoose');

async function connect(uri) {
  mongoose.set('strictQuery', true);
  await mongoose.connect(uri);
}

module.exports = { connect };

src/note.model.js :

const mongoose = require('mongoose');

const NoteSchema = new mongoose.Schema({
  title: { type: String, required: true },
  body: { type: String, required: true }
}, { timestamps: true });

module.exports = mongoose.model('Note', NoteSchema);

Étendez src/routes.js avec de petites routes CRUD :

// add at top
const Note = require('./note.model');

// create note
router.post('/notes', async (req, res, next) => {
  try {
    const schema = z.object({ title: z.string().min(1), body: z.string().min(1) });
    const parsed = schema.safeParse(req.body);
    if (!parsed.success) return next(new HttpError(400, 'Invalid body'));
    const note = await Note.create(parsed.data);
    res.status(201).json({ id: note._id.toString() });
  } catch (e) { next(e); }
});

// list notes
router.get('/notes', async (req, res, next) => {
  try {
    const notes = await Note.find().sort({ createdAt: -1 }).limit(10).lean();
    res.json({ notes });
  } catch (e) { next(e); }
});

Câblez la base dans src/index.js (à ajouter près du haut) :

const { connect } = require('./db');

Et avant app.listen :

async function start() {
  const mongoUri = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/express_lab';
  if (process.env.NODE_ENV !== 'test') {
    await connect(mongoUri);
  }
  app.listen(PORT, HOST, () => {
    logger.info({ PORT, HOST }, 'Server started');
  });
}

start().catch(err => {
  logger.error({ err }, 'Failed to start');
  process.exit(1);
});

Essayez l'API des notes :

curl -s -X POST http://127.0.0.1:4000/api/notes \
  -H 'Content-Type: application/json' \
  -d '{"title":"first","body":"hello"}' | jq .

curl -s http://127.0.0.1:4000/api/notes | jq .
  1. Conseils de reproductibilité
  • Gardez un fichier .env.example dans le dépôt pour documenter les variables requises.
  • Figez la version de Node.js avec un .nvmrc ou l'outil de votre choix.
  • Epinglez les versions majeures des dépendances pour éviter les ruptures surprises.
  • Ajoutez un court README avec les commandes run, test et curl.
  1. Dépannage
  • Port déjà utilisé : changez PORT dans .env ou tuez l'autre processus.
  • Erreurs CORS depuis un front sur un autre port : définissez CORS_ORIGIN dans .env.
  • Erreurs 429 : votre rate limit fonctionne ; réduisez les appels ou augmentez RATE_LIMIT_MAX.
  • Connexion Mongo refusée : assurez-vous que le conteneur tourne et écoute sur 27017.

Plan pilote local

Gardez le premier pilote restreint et vérifiable :

  • Portée : uniquement /health, /echo, /math/sum ; middleware de sécurité ; logging ; 3 tests.
  • Time-box : 60 minutes du zéro aux tests passants.
  • Critères de succès :
  • GET /api/health renvoie status ok en < 50 ms en local
  • POST /api/echo renvoie l'écho d'un message non vide
  • GET /api/math/sum renvoie la somme correcte pour 1,2,3
  • Les logs incluent un request id, généré ou x-request-id
  • Le rate limit renvoie 429 quand le max défini est dépassé
  • npm test passe en moins de 2 secondes sur un portable typique
  • Objectif étendu : ajouter les endpoints /notes avec un MongoDB local et confirmer un CRUD basique.

Conclusion

Vous disposez maintenant d'un Express API local lab compact, sécurisé et testable, qui tourne entièrement sur localhost. La mise en place est claire et reproductible, pour bricoler en sécurité, mesurer les changements et étendre les endpoints selon l'évolution de vos besoins. Prochaines étapes :

  • Étendre les tests pour couvrir les cas limites et les erreurs
  • Introduire une validation de requêtes pour toutes les routes
  • Ajouter une base locale ou une file de messages si nécessaire
  • Regrouper des scripts pour un setup en une commande afin que toute l'équipe puisse exécuter le même lab

Construisez par incréments, vérifiez à chaque étape et gardez le pilote réduit avant d'empiler de la complexité. Ceci reste un Express API setup simple à maintenir, propice à l'Express API testing et riche en Express API examples utiles à votre Express API development.

Article Quality Score

Reader usefulness 100%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL