Skip to content
Learni
View all tutorials
DevOps

Comment créer une CMDB basique avec Node.js en 2026

Introduction

Une CMDB (Configuration Management Database) est le cœur de la gestion ITIL : elle stocke les Configuration Items (CI) comme serveurs, applications ou réseaux, et leurs relations (dépendances, hébergements). Sans elle, tracer les impacts d'un incident devient chaotique, comme naviguer sans carte dans un data center.

Ce tutoriel beginner vous montre comment créer une CMDB fonctionnelle en Node.js avec Express (API REST) et Prisma (ORM sur SQLite). Pourquoi cette stack ? SQLite pour zéro config DB (idéal dev local), Prisma pour schémas type-sûrs, Express pour routes rapides. À la fin, vous avez une API CRUD complète : listez, créez, liez des CI. Exemple concret : modélisez un serveur web hébergeant une app, avec relations automatisées.

Important pour pros : cette base scale vers PostgreSQL. Temps estimé : 30 min. Valeur : bookmarquez pour vos audits ITIL.

Prérequis

  • Node.js 20+ installé (téléchargez ici)
  • Connaissances basiques en JavaScript (variables, fonctions async)
  • Éditeur de code (VS Code recommandé avec extension Prisma)
  • Terminal (PowerShell ou bash)
  • 5 min pour setup

Initialiser le projet et installer les dépendances

terminal
mkdir cmdb-simple
cd cmdb-simple
npm init -y
npm install express @prisma/client prisma typescript ts-node @types/express @types/node
npm install -D @types/node
npx prisma init --datasource-provider sqlite

Cette commande crée le dossier projet, initialise package.json, installe Express pour l'API, Prisma pour l'ORM et TypeScript pour le typage. L'init Prisma génère prisma/schema.prisma et .env avec DATABASE_URL pour SQLite. Exécutez-la d'un coup : votre setup est prêt en 1 min, sans pièges de versions.

Comprendre le schéma CMDB

Avant le code, conceptualisons : une CMDB a CI (ex: 'Serveur-Web-01', type 'Serveur') et Relations (ex: 'héberge' vers 'App-Ecommerce'). Analogie : CI sont des nœuds, relations des arêtes dans un graphe. On utilise deux modèles Prisma liés par foreign keys pour éviter les doublons et assurer l'intégrité.

Définir les modèles Prisma pour CI et Relations

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

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

model CI {
  id          String     @id @default(cuid())
  name        String     @unique
  type        String     // ex: Serveur, Application, Reseau
  description String?
  relationsAsFrom Relation[] @relation("FromTo")
  relationsAsTo   Relation[] @relation("ToFrom")
}

model Relation {
  id       String @id @default(cuid())
  type     String // ex: heberge, depend, connecte
  ciFromId String
  ciToId   String
  ciFrom   CI     @relation("FromTo", fields: [ciFromId], references: [id], onDelete: Cascade)
  ciTo     CI     @relation("ToFrom", fields: [ciToId], references: [id], onDelete: Cascade)

  @@unique([ciFromId, ciToId, type])
}

Ce schéma définit CI avec nom unique et type, plus Relations bidirectionnelles pour graphe. @@unique évite doublons relations. Cascade supprime auto les orphelins. Piège : sans onDelete, suppressions plantent ; testez avec npx prisma db push après édition.

Appliquer la migration et générer le client Prisma

terminal
npx prisma db push
npx prisma generate

db push crée la DB SQLite sans migration formelle (idéal proto). generate compile le client TS. Résultat : ./prisma/dev.db existe avec tables. Vérifiez avec sqlite3 prisma/dev.db ".schema" – zéro erreur si OK.

Créer le serveur Express avec typage TS

Maintenant, l'API : endpoints REST pour CI (list, create) et relations. On utilise async/await pour clarté, Prisma pour queries type-sûres. Port 3001 pour éviter conflits.

Implémenter le serveur principal et routes CI

server.ts
import express from 'express';
import { PrismaClient } from '@prisma/client';

const app = express();
const prisma = new PrismaClient();

app.use(express.json());

const PORT = 3001;

// GET /cis - Liste tous les CI avec relations
app.get('/cis', async (req, res) => {
  try {
    const cis = await prisma.cI.findMany({
      include: {
        relationsAsFrom: true,
        relationsAsTo: true,
      },
    });
    res.json(cis);
  } catch (error) {
    res.status(500).json({ error: 'Erreur serveur' });
  }
});

// POST /ci - Créer un CI
app.post('/ci', async (req, res) => {
  try {
    const { name, type, description } = req.body;
    const ci = await prisma.cI.create({
      data: { name, type, description },
    });
    res.json(ci);
  } catch (error) {
    res.status(400).json({ error: 'CI existant ou données invalides' });
  }
});

app.listen(PORT, () => {
  console.log(`Serveur CMDB sur http://localhost:${PORT}`);
});

Ce serveur expose GET /cis (avec include pour graphe complet) et POST /ci. Try/catch gère erreurs Prisma (ex: unique violation). Lancez avec npx ts-node server.ts – curl http://localhost:3001/cis vide au début. Piège : oubliez express.json() et POST body parse pas.

Ajouter les routes pour les Relations

relations.ts
// Ajoutez ces routes à server.ts après les routes CI

// POST /relation - Créer une relation
app.post('/relation', async (req, res) => {
  try {
    const { type, ciFromId, ciToId } = req.body;
    // Vérif existence CI
    const ciFrom = await prisma.cI.findUnique({ where: { id: ciFromId } });
    const ciTo = await prisma.cI.findUnique({ where: { id: ciToId } });
    if (!ciFrom || !ciTo) {
      return res.status(404).json({ error: 'CI non trouvé' });
    }
    const relation = await prisma.relation.create({
      data: { type, ciFromId, ciToId },
      include: { ciFrom: true, ciTo: true },
    });
    res.json(relation);
  } catch (error) {
    res.status(400).json({ error: 'Relation dupliquée ou invalide' });
  }
});

// DELETE /relation/:id
app.delete('/relation/:id', async (req, res) => {
  try {
    await prisma.relation.delete({ where: { id: req.params.id } });
    res.json({ message: 'Relation supprimée' });
  } catch (error) {
    res.status(404).json({ error: 'Relation non trouvée' });
  }
});

Collez ces routes dans server.ts. POST vérifie CI existants avant création (intégrité). Include renvoie CI liés. DELETE par ID. Test : créez 2 CI, puis relation ; GET /cis montre graphe. Piège : sans vérif, foreign key errors crashent.

Script de seeding pour données d'exemple

seed.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
  // Créer CI exemples
  const serveur = await prisma.cI.create({
    data: { name: 'Serveur-Web-01', type: 'Serveur', description: 'Nginx sur Ubuntu' },
  });
  const app = await prisma.cI.create({
    data: { name: 'App-Ecommerce', type: 'Application', description: 'React + Node' },
  });
  const bd = await prisma.cI.create({
    data: { name: 'DB-Prod-01', type: 'Base de Données', description: 'PostgreSQL' },
  });

  // Créer relations
  await prisma.relation.create({
    data: { type: 'heberge', ciFromId: serveur.id, ciToId: app.id },
  });
  await prisma.relation.create({
    data: { type: 'depend', ciFromId: app.id, ciToId: bd.id },
  });

  console.log('Seed OK: 3 CI, 2 relations');
}

main()
  .finally(async () => {
    await prisma.$disconnect();
  });

Ce script peuple la DB avec exemples réalistes (serveur → app → DB). Lancez npx ts-node seed.ts avant API. Vérifiez : curl GET /cis montre objets nested. Piège : oubliez $disconnect, connexions leak en dev.

Package.json avec scripts prêts

package.json
{
  "name": "cmdb-simple",
  "version": "1.0.0",
  "scripts": {
    "dev": "ts-node server.ts",
    "seed": "ts-node seed.ts",
    "db:push": "prisma db push",
    "generate": "prisma generate"
  },
  "dependencies": {
    "express": "^4.19.2",
    "@prisma/client": "^5.14.0",
    "prisma": "^5.14.0",
    "typescript": "^5.5.3",
    "ts-node": "^10.9.2",
    "@types/express": "^4.17.21",
    "@types/node": "^22.0.0"
  }
}

Copiez ce package.json complet (remplacez l'auto-généré). npm run dev lance serveur, run seed popule. Versions pinned pour reproductibilité 2026. Piège : deps manquantes cassent ts-node.

Tester votre CMDB

  • Lancez npm run seed
  • npm run dev
  • curl -X POST http://localhost:3001/ci -H "Content-Type: application/json" -d '{"name":"Reseau-01","type":"Reseau"}'
  • curl http://localhost:3001/cis (voir graphe)
  • curl -X POST http://localhost:3001/relation -H "Content-Type: application/json" -d '{"type":"connecte","ciFromId":"","ciToId":""}'
Outils bonus : Postman collection ou Insomnia.

Bonnes pratiques

  • Validation inputs : Ajoutez Zod pour schemas (npm i zod) – ex: z.object({name: z.string().min(1)})
  • Pagination : Sur GET /cis, ajoutez skip/take pour >100 CI
  • Auth : Intégrez JWT (npm i jsonwebtoken) sur routes sensibles
  • Logs : Utilisez Winston pour tracer queries Prisma
  • Backup : Script cron copiant prisma/dev.db

Erreurs courantes à éviter

  • Oubli include : GET /cis sans relations montre CI isolés – toujours nested pour vue graphe
  • Migrations manuelles : db push OK dev, mais utilisez migrate deploy en prod
  • Connexions leak : Toujours prisma.$disconnect() en scripts
  • CORS : Ajoutez app.use(cors()) si frontend distant
  • Unique violations : Testez noms CI uniques avant POST

Pour aller plus loin

  • Migrez vers PostgreSQL : changez url en DB prod
  • Visualisez graphe : Intégrez Cytoscape.js frontend
  • ITIL avancé : Ajoutez Status, Owner fields
  • Outils pros : ServiceNow API ou iTop open-source
Découvrez nos formations DevOps et ITIL pour scaler votre CMDB en entreprise.