diff --git a/.gitignore b/.gitignore index ccbf371..d75a511 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. +# dotenv +.env + # compiled output dist tmp diff --git a/apps/ebitemp-api/src/app/app.module.ts b/apps/ebitemp-api/src/app/app.module.ts index 8662803..4cff5bd 100644 --- a/apps/ebitemp-api/src/app/app.module.ts +++ b/apps/ebitemp-api/src/app/app.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; +import { AppConfigModule } from './modules/config/config.module'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ - imports: [], + imports: [AppConfigModule], controllers: [AppController], providers: [AppService], }) diff --git a/apps/ebitemp-api/src/app/config/local.config.ts b/apps/ebitemp-api/src/app/config/local.config.ts new file mode 100644 index 0000000..844c438 --- /dev/null +++ b/apps/ebitemp-api/src/app/config/local.config.ts @@ -0,0 +1,19 @@ +import coerceRecordTypes from '../modules/config/coerce-record-types'; +import { registerAs } from '@nestjs/config'; +import { z } from 'zod'; + +export const localSchema = z.object({ + production: z.boolean(), + port: z.number().optional(), +}); +export type LocalConfig = z.infer; + +export const localConfig = registerAs('local', () => { + const env = coerceRecordTypes(process.env); + + const config: LocalConfig = localSchema.strict().parse({ + production: env['PRODUCTION'], + port: env['PORT'], + }); + return config; +}); diff --git a/apps/ebitemp-api/src/app/modules/config/coerce-record-types.ts b/apps/ebitemp-api/src/app/modules/config/coerce-record-types.ts new file mode 100644 index 0000000..7a63ba9 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/config/coerce-record-types.ts @@ -0,0 +1,11 @@ +import { clone, each } from 'lodash'; + +export default (raw: Record): Record => { + raw = clone(raw); + return each(raw, (value, key) => { + if (value === 'true') raw[key] = true; + if (value === 'false') raw[key] = false; + if (value === 'null') raw[key] = null; + if (!isNaN(Number(value))) raw[key] = Number(value); + }); +}; diff --git a/apps/ebitemp-api/src/app/modules/config/config.module.ts b/apps/ebitemp-api/src/app/modules/config/config.module.ts new file mode 100644 index 0000000..b0bccde --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/config/config.module.ts @@ -0,0 +1,15 @@ + +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { localConfig } from '../../config/local.config'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + expandVariables: true, + load: [localConfig], + }), + ], +}) +export class AppConfigModule {} diff --git a/apps/ebitemp-api/src/main.ts b/apps/ebitemp-api/src/main.ts index 2c9e0ea..fdd9a27 100644 --- a/apps/ebitemp-api/src/main.ts +++ b/apps/ebitemp-api/src/main.ts @@ -6,12 +6,16 @@ import { Logger } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app/app.module'; +import { LocalConfig, localConfig } from './app/config/local.config'; async function bootstrap() { const app = await NestFactory.create(AppModule); const globalPrefix = 'api'; app.setGlobalPrefix(globalPrefix); - const port = process.env.PORT || 3000; + + const config = app.get(localConfig.KEY); + const port = config.port || 3000; + await app.listen(port); Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`); } diff --git a/package.json b/package.json index f39d60a..8307bff 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,16 @@ "private": true, "dependencies": { "@nestjs/common": "^10.0.2", + "@nestjs/config": "^4.0.0", "@nestjs/core": "^10.0.2", "@nestjs/platform-express": "^10.0.2", "@nx/devkit": "20.4.1", "axios": "^1.6.0", + "lodash": "^4.17.21", "patch-package": "^8.0.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.0" + "rxjs": "^7.8.0", + "zod": "^3.24.1" }, "devDependencies": { "@eslint/js": "^9.8.0", @@ -38,6 +41,7 @@ "@swc/core": "~1.5.7", "@swc/helpers": "~0.5.11", "@types/jest": "^29.5.12", + "@types/lodash": "^4.17.15", "@types/node": "~18.16.9", "eslint": "^9.8.0", "eslint-config-prettier": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7ca8fd..4c4c8e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@nestjs/common': specifier: ^10.0.2 version: 10.4.15(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/config': + specifier: ^4.0.0 + version: 4.0.0(@nestjs/common@10.4.15)(rxjs@7.8.1) '@nestjs/core': specifier: ^10.0.2 version: 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.1.14)(rxjs@7.8.1) @@ -20,6 +23,9 @@ dependencies: axios: specifier: ^1.6.0 version: 1.7.9 + lodash: + specifier: ^4.17.21 + version: 4.17.21 patch-package: specifier: ^8.0.0 version: 8.0.0 @@ -29,6 +35,9 @@ dependencies: rxjs: specifier: ^7.8.0 version: 7.8.1 + zod: + specifier: ^3.24.1 + version: 3.24.1 devDependencies: '@eslint/js': @@ -85,6 +94,9 @@ devDependencies: '@types/jest': specifier: ^29.5.12 version: 29.5.14 + '@types/lodash': + specifier: ^4.17.15 + version: 4.17.15 '@types/node': specifier: ~18.16.9 version: 18.16.20 @@ -2310,6 +2322,19 @@ packages: tslib: 2.8.1 uid: 2.0.2 + /@nestjs/config@4.0.0(@nestjs/common@10.4.15)(rxjs@7.8.1): + resolution: {integrity: sha512-hyhUMtVwlT+tavtPNyekl8iP0QTU1U6awKrgdOSxhMhp3TQMltx7hz2yqGTcARp+19zWPfgJudyxthuD3lPp/Q==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + rxjs: ^7.1.0 + dependencies: + '@nestjs/common': 10.4.15(reflect-metadata@0.1.14)(rxjs@7.8.1) + dotenv: 16.4.7 + dotenv-expand: 12.0.1 + lodash: 4.17.21 + rxjs: 7.8.1 + dev: false + /@nestjs/core@10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.1.14)(rxjs@7.8.1): resolution: {integrity: sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==} requiresBuild: true @@ -3425,6 +3450,10 @@ packages: '@types/node': 18.16.20 dev: true + /@types/lodash@4.17.15: + resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==} + dev: true + /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true @@ -5213,6 +5242,13 @@ packages: dependencies: dotenv: 16.4.7 + /dotenv-expand@12.0.1: + resolution: {integrity: sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==} + engines: {node: '>=12'} + dependencies: + dotenv: 16.4.7 + dev: false + /dotenv@16.4.7: resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} engines: {node: '>=12'} @@ -7371,7 +7407,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -10512,3 +10547,7 @@ packages: /zen-observable@0.8.15: resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} dev: true + + /zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + dev: false