chore(backend): bootstrap Express + TypeScript + Prisma

This commit is contained in:
SaidSoighiri94
2026-06-04 13:31:32 +02:00
parent 544e3edc04
commit bb93245fa4
7 changed files with 217 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
{
"name": "eme-backend",
"version": "1.0.0",
"description": "EME — Emprunt Matériel ENSUP — API REST",
"main": "dist/app.js",
"scripts": {
"build": "tsc",
"start": "node dist/app.js",
"dev": "nodemon --exec ts-node src/app.ts",
"prisma:generate": "prisma generate",
"prisma:migrate": "prisma migrate dev",
"prisma:studio": "prisma studio"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^25.9.1",
"nodemon": "^3.1.14",
"prisma": "^7.8.0",
"ts-node": "^10.9.2",
"typescript": "^6.0.3"
},
"dependencies": {
"@prisma/adapter-mssql": "^7.8.0",
"@prisma/client": "^7.8.0",
"cors": "^2.8.6",
"dotenv": "^17.4.2",
"express": "^5.2.1",
"jsonwebtoken": "^9.0.3"
}
}
+12
View File
@@ -0,0 +1,12 @@
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
},
});
+33
View File
@@ -0,0 +1,33 @@
import express from 'express';
import cors from 'cors';
import { env } from './config/env';
import { connectDatabase, disconnectDatabase } from './db/prisma';
const app = express();
// Middlewares globaux
app.use(cors());
app.use(express.json());
// Route de test
app.get('/health', (_req, res) => {
res.json({ status: 'ok', app: 'EME API' });
});
async function bootstrap(): Promise<void> {
await connectDatabase();
const server = app.listen(env.port, () => {
console.log(`[EME API] Serveur demarre sur http://localhost:${env.port}`);
});
process.on('SIGINT', async () => {
await disconnectDatabase();
server.close(() => process.exit(0));
});
}
bootstrap().catch((error: unknown) => {
console.error('[EME API] Echec du demarrage', error);
process.exit(1);
});
+47
View File
@@ -0,0 +1,47 @@
import dotenv from 'dotenv';
dotenv.config();
function required(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Variable d'environnement manquante: ${name}`);
}
return value;
}
function numberFromEnv(name: string, defaultValue: number): number {
const value = process.env[name];
if (!value) {
return defaultValue;
}
const parsed = Number(value);
if (Number.isNaN(parsed)) {
throw new Error(`Variable d'environnement invalide: ${name}`);
}
return parsed;
}
export const env = {
port: numberFromEnv('PORT', 3000),
nodeEnv: process.env.NODE_ENV || 'development',
frontendUrl: required('FRONTEND_URL'),
databaseUrl: required('DATABASE_URL'),
db: {
server: required('DB_SERVER'),
port: numberFromEnv('DB_PORT', 1433),
name: required('DB_NAME'),
user: required('DB_USER'),
password: required('DB_PASSWORD'),
},
azure: {
tenantId: required('AZURE_TENANT_ID'),
clientId: required('AZURE_CLIENT_ID'),
},
};
+31
View File
@@ -0,0 +1,31 @@
import { PrismaClient } from '@prisma/client';
import { PrismaMssql } from '@prisma/adapter-mssql';
import { env } from '../config/env';
const adapter = new PrismaMssql({
server: env.db.server,
port: env.db.port,
database: env.db.name,
user: env.db.user,
password: env.db.password,
options: {
encrypt: true,
trustServerCertificate: true,
},
pool: {
max: 10,
min: 0,
idleTimeoutMillis: 30000,
},
});
export const prisma = new PrismaClient({ adapter });
export async function connectDatabase(): Promise<void> {
await prisma.$connect();
console.log('[DB] Connexion SQL Server via Prisma etablie');
}
export async function disconnectDatabase(): Promise<void> {
await prisma.$disconnect();
}
+41
View File
@@ -0,0 +1,41 @@
export const STATUT_MATERIEL = [
'DISPONIBLE',
'EMPRUNTE',
'NON_CONFORME',
'DETERIORE',
'MAINTENANCE',
'INDISPONIBLE',
] as const;
export type StatutMateriel = (typeof STATUT_MATERIEL)[number];
export const STATUT_EMPRUNT = [
'EN_COURS',
'EN_RETARD',
'CLOTURE',
'RETOUR_NON_CONFORME',
'ANNULE',
] as const;
export type StatutEmprunt = (typeof STATUT_EMPRUNT)[number];
export const TYPE_CHECKLIST = ['DEPART', 'RETOUR'] as const;
export type TypeChecklist = (typeof TYPE_CHECKLIST)[number];
export const ETAT_CHECKLIST_ELEMENT = ['PRESENT', 'ABSENT', 'DETERIORE'] as const;
export type EtatChecklistElement = (typeof ETAT_CHECKLIST_ELEMENT)[number];
export const STATUT_ANOMALIE = [
'DETECTEE',
'EN_COURS_TRAITEMENT',
'RESOLUE',
'CLOTUREE',
] as const;
export type StatutAnomalie = (typeof STATUT_ANOMALIE)[number];
export const MODE_IDENTIFICATION = ['QR_CODE', 'MAIL_ENSUP'] as const;
export type ModeIdentification = (typeof MODE_IDENTIFICATION)[number];
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"sourceMap": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}