From 152e5bcfe0edbe839afc2ad50ee09b44ea873aac Mon Sep 17 00:00:00 2001 From: LuckMeelo Date: Mon, 11 Aug 2025 12:25:19 +0200 Subject: [PATCH] feat: added .env configuration for global app, database and jwt Refs: #2 --- .env.example | 15 ++++ package-lock.json | 117 +++++++++++++++++++++++++++++++- package.json | 2 + src/app.module.ts | 19 +++++- src/config/app.config.ts | 6 ++ src/config/database.config.ts | 9 +++ src/config/jwt.config.ts | 6 ++ src/config/validation.schema.ts | 19 ++++++ src/main.ts | 14 +++- 9 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 .env.example create mode 100644 src/config/app.config.ts create mode 100644 src/config/database.config.ts create mode 100644 src/config/jwt.config.ts create mode 100644 src/config/validation.schema.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ea45a0d --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Fichier: .env.example + +# Configuration de la base de données PostgreSQL +POSTGRES_HOST= +POSTGRES_PORT= +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= + +# Configuration de l'API +API_PORT=3000 + +# Secrets pour l'authentification JWT +JWT_SECRET= +JWT_EXPIRATION_TIME= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 58b4cbb..b4503f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,10 @@ "license": "UNLICENSED", "dependencies": { "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.0.1", + "joi": "^18.0.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, @@ -938,6 +940,54 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.2.tgz", + "integrity": "sha512-1jkwm1WY9VIb6WhdANRmWDkXQUcIRpxqZpSdS+HD9vhoVL3zwoFvP8doQNEgT6k3VST0Ewu50wZnXIceRYp5tw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2285,6 +2335,21 @@ } } }, + "node_modules/@nestjs/config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.2.tgz", + "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.7", + "dotenv-expand": "12.0.1", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/core": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.5.tgz", @@ -2520,6 +2585,12 @@ "@sinonjs/commons": "^3.0.1" } }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@tokenizer/inflate": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", @@ -4757,6 +4828,33 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", + "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -7079,6 +7177,24 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joi": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.0.tgz", + "integrity": "sha512-fpbpXN/TD04Xz1/cCXzUR3ghDkhyiHjbzTILx3wNyKXIzQJ55uYAkUGWwhX72uHge/6MdFA/kp1ZUh35DlYmaA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.0.0" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7263,7 +7379,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { diff --git a/package.json b/package.json index bcfae1b..cd745ee 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ }, "dependencies": { "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.0.1", + "joi": "^18.0.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, diff --git a/src/app.module.ts b/src/app.module.ts index 8662803..4d5d9b2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,9 +1,26 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import appConfig from './config/app.config'; +import databaseConfig from './config/database.config'; +import jwtConfig from './config/jwt.config'; +import { configValidationSchema } from './config/validation.schema'; + @Module({ - imports: [], + imports: [ + ConfigModule.forRoot({ + // Gestion dynamique des fichiers .env + envFilePath: [`.env.${process.env.NODE_ENV || 'development'}`, '.env'], + + // Chargement de configurations typées + load: [appConfig, databaseConfig, jwtConfig], + + isGlobal: true, + validationSchema: configValidationSchema, + }), + ], controllers: [AppController], providers: [AppService], }) diff --git a/src/config/app.config.ts b/src/config/app.config.ts new file mode 100644 index 0000000..ddd91e4 --- /dev/null +++ b/src/config/app.config.ts @@ -0,0 +1,6 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs('', () => ({ + port: process.env.PORT, + env: process.env.NODE_ENV, +})); diff --git a/src/config/database.config.ts b/src/config/database.config.ts new file mode 100644 index 0000000..163bf39 --- /dev/null +++ b/src/config/database.config.ts @@ -0,0 +1,9 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs('database', () => ({ + host: process.env.POSTGRES_HOST, + port: process.env.POSTGRES_PORT, + username: process.env.POSTGRES_USER, + password: process.env.POSTGRES_PASSWORD, + database: process.env.POSTGRES_DB, +})); diff --git a/src/config/jwt.config.ts b/src/config/jwt.config.ts new file mode 100644 index 0000000..7a26615 --- /dev/null +++ b/src/config/jwt.config.ts @@ -0,0 +1,6 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs('jwt', () => ({ + secret: process.env.JWT_SECRET, + expiresIn: process.env.JWT_EXPIRATION_TIME, +})); diff --git a/src/config/validation.schema.ts b/src/config/validation.schema.ts new file mode 100644 index 0000000..f5665fc --- /dev/null +++ b/src/config/validation.schema.ts @@ -0,0 +1,19 @@ +import * as Joi from 'joi'; + +export const configValidationSchema = Joi.object({ + NODE_ENV: Joi.string() + .valid('development', 'production', 'test') + .default('development'), + PORT: Joi.number().optional(), + + // Base de données + POSTGRES_HOST: Joi.string().required(), + POSTGRES_PORT: Joi.number().required(), + POSTGRES_USER: Joi.string().required(), + POSTGRES_PASSWORD: Joi.string().required(), + POSTGRES_DB: Joi.string().required(), + + // JWT + JWT_SECRET: Joi.string().required(), + JWT_EXPIRATION_TIME: Joi.string().required(), +}); diff --git a/src/main.ts b/src/main.ts index f76bc8d..737dd29 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,18 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import { ConfigService } from '@nestjs/config'; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(process.env.PORT ?? 3000); + + const configService = app.get(ConfigService); + + const port = configService.get('app.port', 3000); + await app.listen(port); + console.log(`✅ P'titsPas API is running on: ${await app.getUrl()}`); } -bootstrap(); + +bootstrap().catch((err) => { + console.error('❌ Error starting the application:', err); + process.exit(1); +});