DEV Community

Lucas M Dev
Lucas M Dev

Posted on • Originally published at lucasmdevdev.github.io

Créer une API REST avec Node.js en 2026 : Guide Complet

Créer une API REST avec Node.js et Express en 2026 : Guide Complet

Node.js + Express reste en 2026 l'une des stacks les plus utilisées pour créer des APIs backend. Ce guide vous montre comment créer une API REST complète de zéro : structure du projet, CRUD, validation, authentification JWT, et déploiement.

Setup du projet

# Créer le projet
mkdir mon-api && cd mon-api
npm init -y

# Installer les dépendances
npm install express cors helmet dotenv
npm install --save-dev typescript ts-node-dev @types/express @types/cors @types/node

# Initialiser TypeScript
npx tsc --init
Enter fullscreen mode Exit fullscreen mode

Configuration TypeScript minimale

// tsconfig.json
{
 "compilerOptions": {
 "target": "ES2022",
 "module": "commonjs",
 "outDir": "./dist",
 "rootDir": "./src",
 "strict": true,
 "esModuleInterop": true,
 "resolveJsonModule": true
 }
}
Enter fullscreen mode Exit fullscreen mode

Structure du projet recommandée

mon-api/
├── src/
│ ├── controllers/ # Logique métier
│ │ └── users.controller.ts
│ ├── routes/ # Définition des routes
│ │ └── users.routes.ts
│ ├── middleware/ # Middlewares (auth, validation)
│ │ └── auth.middleware.ts
│ ├── types/ # Types TypeScript
│ │ └── index.ts
│ └── index.ts # Point d'entrée
├── .env
├── package.json
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Point d'entrée — src/index.ts

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import dotenv from 'dotenv';
import usersRouter from './routes/users.routes';

dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// Middlewares
app.use(helmet()); // Headers de sécurité
app.use(cors()); // Cross-Origin Resource Sharing
app.use(express.json()); // Parser le JSON

// Routes
app.use('/api/users', usersRouter);

// Health check
app.get('/health', (req, res) => {
 res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// Gestion des erreurs globale
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
 console.error(err.stack);
 res.status(500).json({ error: 'Erreur interne du serveur' });
});

app.listen(PORT, () => {
 console.log(`🚀 API démarrée sur http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Routes CRUD

// src/routes/users.routes.ts
import { Router } from 'express';
import {
 getAllUsers, getUserById, createUser, updateUser, deleteUser
} from '../controllers/users.controller';
import { authMiddleware } from '../middleware/auth.middleware';

const router = Router();

router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
router.put('/:id', authMiddleware, updateUser); // Protégé
router.delete('/:id', authMiddleware, deleteUser); // Protégé

export default router;
Enter fullscreen mode Exit fullscreen mode
// src/controllers/users.controller.ts
import { Request, Response } from 'express';

// Simulation d'une base de données en mémoire
let users = [
 { id: 1, name: 'Alice', email: 'alice@example.com' },
 { id: 2, name: 'Bob', email: 'bob@example.com' },
];
let nextId = 3;

export const getAllUsers = (req: Request, res: Response) => {
 const { page = '1', limit = '10' } = req.query;
 const p = parseInt(page as string);
 const l = parseInt(limit as string);
 const start = (p - 1) * l;
 const paginated = users.slice(start, start + l);

 res.json({
 data: paginated,
 total: users.length,
 page: p,
 totalPages: Math.ceil(users.length / l)
 });
};

export const getUserById = (req: Request, res: Response) => {
 const user = users.find(u => u.id === parseInt(req.params.id));
 if (!user) return res.status(404).json({ error: 'Utilisateur non trouvé' });
 res.json(user);
};

export const createUser = (req: Request, res: Response) => {
 const { name, email } = req.body;

 if (!name || !email) {
 return res.status(400).json({ error: 'name et email sont requis' });
 }

 if (users.some(u => u.email === email)) {
 return res.status(409).json({ error: 'Email déjà utilisé' });
 }

 const newUser = { id: nextId++, name, email };
 users.push(newUser);
 res.status(201).json(newUser);
};

export const updateUser = (req: Request, res: Response) => {
 const idx = users.findIndex(u => u.id === parseInt(req.params.id));
 if (idx === -1) return res.status(404).json({ error: 'Utilisateur non trouvé' });

 users[idx] = { ...users[idx], ...req.body, id: users[idx].id };
 res.json(users[idx]);
};

export const deleteUser = (req: Request, res: Response) => {
 const idx = users.findIndex(u => u.id === parseInt(req.params.id));
 if (idx === -1) return res.status(404).json({ error: 'Utilisateur non trouvé' });

 users.splice(idx, 1);
 res.status(204).send();
};
Enter fullscreen mode Exit fullscreen mode

Validation avec Zod

Zod est la bibliothèque de validation recommandée en 2026. Elle s'intègre parfaitement avec TypeScript.

npm install zod
Enter fullscreen mode Exit fullscreen mode
import { z } from 'zod';

// Schéma de validation
const createUserSchema = z.object({
 name: z.string().min(2, 'Le nom doit faire au moins 2 caractères').max(100),
 email: z.string().email('Email invalide'),
 age: z.number().min(18).max(120).optional()
});

// Middleware de validation
export const validateBody = (schema: z.ZodSchema) => {
 return (req: Request, res: Response, next: NextFunction) => {
 const result = schema.safeParse(req.body);
 if (!result.success) {
 return res.status(400).json({
 error: 'Données invalides',
 details: result.error.flatten().fieldErrors
 });
 }
 req.body = result.data;
 next();
 };
};

// Dans la route
router.post('/', validateBody(createUserSchema), createUser);
Enter fullscreen mode Exit fullscreen mode

Authentification avec JWT

npm install jsonwebtoken bcryptjs
npm install --save-dev @types/jsonwebtoken @types/bcryptjs
Enter fullscreen mode Exit fullscreen mode
// src/middleware/auth.middleware.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

interface JWTPayload {
 userId: number;
 email: string;
}

declare global {
 namespace Express {
 interface Request {
 user?: JWTPayload;
 }
 }
}

export const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
 const token = req.headers.authorization?.split(' ')[1]; // Bearer TOKEN

 if (!token) {
 return res.status(401).json({ error: 'Token manquant' });
 }

 try {
 const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
 req.user = payload;
 next();
 } catch (err) {
 return res.status(401).json({ error: 'Token invalide ou expiré' });
 }
};

// Route de login
export const loginHandler = async (req: Request, res: Response) => {
 const { email, password } = req.body;

 // Vérifier l'utilisateur en base de données...
 const user = await findUserByEmail(email);
 if (!user || !await bcrypt.compare(password, user.passwordHash)) {
 return res.status(401).json({ error: 'Identifiants invalides' });
 }

 const token = jwt.sign(
 { userId: user.id, email: user.email },
 process.env.JWT_SECRET!,
 { expiresIn: '7d' }
 );

 res.json({ token, user: { id: user.id, email: user.email } });
};
Enter fullscreen mode Exit fullscreen mode

Base de données avec Prisma

Prisma est l'ORM recommandé en 2026 pour Node.js. Type-safe, performant, et excellent DX.

npm install prisma @prisma/client
npx prisma init
Enter fullscreen mode Exit fullscreen mode
// prisma/schema.prisma
generator client {
 provider = "prisma-client-js"
}

datasource db {
 provider = "postgresql"
 url = env("DATABASE_URL")
}

model User {
 id Int @id @default(autoincrement())
 email String @unique
 name String
 createdAt DateTime @default(now())
 updatedAt DateTime @updatedAt
 posts Post[]
}

model Post {
 id Int @id @default(autoincrement())
 title String
 content String?
 published Boolean @default(false)
 author User @relation(fields: [authorId], references: [id])
 authorId Int
 createdAt DateTime @default(now())
}
Enter fullscreen mode Exit fullscreen mode
# Créer et appliquer la migration
npx prisma migrate dev --name init

# Générer le client Prisma
npx prisma generate
Enter fullscreen mode Exit fullscreen mode

Déploiement sur Railway

# Dockerfile minimal (optionnel mais recommandé)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist/ ./dist/
EXPOSE 3000
CMD ["node", "dist/index.js"]
Enter fullscreen mode Exit fullscreen mode

Avec Railway, le déploiement est simple :

  • Pushez votre code sur GitHub
  • Connectez Railway à votre repo
  • Railway détecte Node.js automatiquement
  • Ajoutez les variables d'environnement (JWT_SECRET, DATABASE_URL)
  • Déploiement automatique à chaque push

Commande build à configurer dans Railway :

npm run build
Enter fullscreen mode Exit fullscreen mode

Commande start :

node dist/index.js
Enter fullscreen mode Exit fullscreen mode

→ Déployer gratuitement avec Railway, Render, Fly.io
→ TypeScript pour débutants en 2026

Top comments (0)