From 6ec09d4c63a81f815590838ff2e9e240fe06124a Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Tue, 19 May 2026 10:42:41 -0300 Subject: [PATCH 1/5] chore: move swagger document from `app.ts` to `main.ts` --- src/app/app.ts | 30 +----------------------------- src/app/main.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/app/app.ts b/src/app/app.ts index b93119b..38d031f 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,10 +1,8 @@ import { NestFactory } from '@nestjs/core'; import type { ExpressAdapter } from '@nestjs/platform-express'; import { NestExpressApplication } from '@nestjs/platform-express'; -import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger'; import cookieParser from 'cookie-parser'; import { Logger } from 'nestjs-pino'; -import { cleanupOpenApiDoc } from 'nestjs-zod'; import { EnvService } from '@/env/env.service'; @@ -20,7 +18,6 @@ export async function createNestApp(adapter?: ExpressAdapter) { }); const envService = app.get(EnvService); - const enableNestLogs = envService.get('ENABLE_NEST_LOGS'); app.enableCors({ origin: envService.get('APP_URL'), @@ -30,35 +27,10 @@ export async function createNestApp(adapter?: ExpressAdapter) { }); app.use(cookieParser(envService.get('COOKIE_SECRET'))); + const enableNestLogs = envService.get('ENABLE_NEST_LOGS'); if (enableNestLogs) { app.useLogger(app.get(Logger)); } - const config = new DocumentBuilder() - .setTitle('SVM - Sistema Viver Melhor') - .setDescription( - 'Esta documentação lista as rotas disponíveis da aplicação, bem como seus respectivos requisitos e dados retornados.', - ) - .setVersion('0.0.1') - .build(); - - const cleanupSwagger = cleanupOpenApiDoc as unknown as ( - doc: OpenAPIObject, - ) => OpenAPIObject; - - const swaggerDocument = SwaggerModule.createDocument(app, config); - const cleanedDocument = cleanupSwagger(swaggerDocument); - - SwaggerModule.setup('/swagger', app, cleanedDocument, { - swaggerOptions: { - withCredentials: true, - persistAuthorization: true, - requestInterceptor: (request: { credentials?: string }) => { - request.credentials = 'include'; - return request; - }, - }, - }); - return app; } diff --git a/src/app/main.ts b/src/app/main.ts index c3f94cc..c9143f0 100644 --- a/src/app/main.ts +++ b/src/app/main.ts @@ -1,3 +1,6 @@ +import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger'; +import { cleanupOpenApiDoc } from 'nestjs-zod'; + import { EnvService } from '@/env/env.service'; import { createNestApp } from './app'; @@ -5,6 +8,32 @@ import { createNestApp } from './app'; async function bootstrap(): Promise { const app = await createNestApp(); + const config = new DocumentBuilder() + .setTitle('SVM - Sistema Viver Melhor') + .setDescription( + 'Esta documentação lista as rotas disponíveis da aplicação, bem como seus respectivos requisitos e dados retornados.', + ) + .setVersion('0.0.1') + .build(); + + const cleanupSwagger = cleanupOpenApiDoc as unknown as ( + doc: OpenAPIObject, + ) => OpenAPIObject; + + const swaggerDocument = SwaggerModule.createDocument(app, config); + const cleanedDocument = cleanupSwagger(swaggerDocument); + + SwaggerModule.setup('/swagger', app, cleanedDocument, { + swaggerOptions: { + withCredentials: true, + persistAuthorization: true, + requestInterceptor: (request: { credentials?: string }) => { + request.credentials = 'include'; + return request; + }, + }, + }); + const envService = app.get(EnvService); const BASE_URL = envService.get('API_BASE_URL'); From 1253eb3506b5c2574b1c85531b6d0e446dff6b61 Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Tue, 19 May 2026 10:44:18 -0300 Subject: [PATCH 2/5] chore: disable `callbackWaitsForEmptyEventLoop` to improve cold starts --- src/app/lambda.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/lambda.ts b/src/app/lambda.ts index 33b5c28..7d4bbe0 100644 --- a/src/app/lambda.ts +++ b/src/app/lambda.ts @@ -16,6 +16,8 @@ export const handler = async ( context: Context, callback: Callback, ) => { + context.callbackWaitsForEmptyEventLoop = false; + if (!cachedHandler) { const expressApp = express(); const adapter = new ExpressAdapter(expressApp); From ee5d676c3dba7ad013ad89ea5a48ea9085757697 Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Tue, 19 May 2026 10:45:50 -0300 Subject: [PATCH 3/5] build(scripts): enable `minify` and `treeShaking` on `build-lambda.ts` --- infra/scripts/build-lambda.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/infra/scripts/build-lambda.ts b/infra/scripts/build-lambda.ts index 49ca32e..f7e34ca 100644 --- a/infra/scripts/build-lambda.ts +++ b/infra/scripts/build-lambda.ts @@ -25,14 +25,15 @@ async function buildLambda() { platform: 'node', target: 'node22', outfile: path.join(outDir, 'index.js'), - minify: false, - treeShaking: false, + minify: true, + treeShaking: true, sourcemap: false, external: [ 'class-transformer/storage', '@nestjs/microservices', '@nestjs/websockets/socket-module', '@nestjs/microservices/microservices-module', + 'pino-pretty', ], }); From ce9e6cc7e1ee13da10d69eb987330dbda5ec167a Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Tue, 19 May 2026 16:56:52 -0300 Subject: [PATCH 4/5] chore(lambda): move adaptar creation to module scope --- src/app/lambda.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/lambda.ts b/src/app/lambda.ts index 7d4bbe0..1e25e0a 100644 --- a/src/app/lambda.ts +++ b/src/app/lambda.ts @@ -2,6 +2,7 @@ import 'reflect-metadata'; import type { RequestListener } from 'node:http'; +import { INestApplication } from '@nestjs/common'; import { ExpressAdapter } from '@nestjs/platform-express'; import serverlessExpress from '@vendia/serverless-express'; import { APIGatewayProxyEvent, Callback, Context } from 'aws-lambda'; @@ -10,6 +11,10 @@ import express from 'express'; import { createNestApp } from './app'; let cachedHandler: ReturnType; +let app: INestApplication; + +const expressApp = express(); +const adapter = new ExpressAdapter(expressApp); export const handler = async ( event: APIGatewayProxyEvent, @@ -19,9 +24,7 @@ export const handler = async ( context.callbackWaitsForEmptyEventLoop = false; if (!cachedHandler) { - const expressApp = express(); - const adapter = new ExpressAdapter(expressApp); - const app = await createNestApp(adapter); + app = await createNestApp(adapter); await app.init(); From 3e4f20db4ccd5f8d572d48bcc3c8fa5f5ca3d1fd Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Tue, 19 May 2026 17:27:29 -0300 Subject: [PATCH 5/5] chore(database): simplify typeorm config in database module --- src/app/database/database.module.ts | 29 +-------- src/config/database.config.ts | 16 ----- src/config/typeorm.config.ts | 88 ++++++++++++---------------- tests/config/test-database.module.ts | 10 ---- 4 files changed, 39 insertions(+), 104 deletions(-) delete mode 100644 src/config/database.config.ts delete mode 100644 tests/config/test-database.module.ts diff --git a/src/app/database/database.module.ts b/src/app/database/database.module.ts index 72d578c..7483a2e 100644 --- a/src/app/database/database.module.ts +++ b/src/app/database/database.module.ts @@ -1,8 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import { testTypeOrmConfig } from '@/config/typeorm.config'; +import { getTypeOrmConfig } from '@/config/typeorm.config'; import { EnvModule } from '@/env/env.module'; import { EnvService } from '@/env/env.service'; @@ -11,31 +10,7 @@ import { EnvService } from '@/env/env.service'; TypeOrmModule.forRootAsync({ imports: [EnvModule], inject: [EnvService], - useFactory: (env: EnvService) => { - if (env.get('NODE_ENV') === 'test') { - return testTypeOrmConfig(); - } - - // Use regular configuration for all other environments - return { - type: 'mysql', - host: env.get('DB_HOST'), - port: env.get('DB_PORT'), - database: env.get('DB_DATABASE'), - username: env.get('DB_USERNAME'), - password: env.get('DB_PASSWORD'), - migrations: [__dirname + 'infra/database/migrations/**/*.ts'], - namingStrategy: new SnakeNamingStrategy(), - autoLoadEntities: true, - synchronize: false, - retryAttempts: 3, - retryDelay: 3000, - extra: { - connectionLimit: 10, - connectTimeout: 10000, - }, - }; - }, + useFactory: (env: EnvService) => getTypeOrmConfig(env), }), ], }) diff --git a/src/config/database.config.ts b/src/config/database.config.ts deleted file mode 100644 index 92368b6..0000000 --- a/src/config/database.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface DatabaseConfig { - host: string; - port: number; - username: string; - password: string; - database: string; - schema?: string; -} - -export const databaseConfig = (): DatabaseConfig => ({ - host: process.env.DB_HOST || 'localhost', - port: parseInt(process.env.DB_PORT || '3306'), - username: process.env.DB_USERNAME || 'root', - password: process.env.DB_PASSWORD || '', - database: process.env.DB_DATABASE || 'test', -}); diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 1072e16..c4b9490 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -2,63 +2,49 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm'; import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; import { DATABASE_ENTITIES } from '@/domain/entities/database'; +import { EnvService } from '@/env/env.service'; -import { databaseConfig } from './database.config'; +export function getTypeOrmConfig(env: EnvService): TypeOrmModuleOptions { + const isTestEnv = env.get('NODE_ENV') === 'test'; -export const typeOrmConfig = (): TypeOrmModuleOptions => { - const config = databaseConfig(); - - return { + const baseConfig: TypeOrmModuleOptions = { type: 'mysql', - host: config.host, - port: config.port, - username: config.username, - password: config.password, - database: config.database, - autoLoadEntities: true, - migrations: [__dirname + '/../infra/database/migrations/**/*.ts'], - synchronize: process.env.NODE_ENV !== 'production', - logging: process.env.NODE_ENV === 'development', + host: env.get('DB_HOST'), + port: env.get('DB_PORT'), + database: env.get('DB_DATABASE'), + username: env.get('DB_USERNAME'), + password: env.get('DB_PASSWORD'), + migrations: [__dirname + '/infra/database/migrations/**/*.ts'], namingStrategy: new SnakeNamingStrategy(), - ssl: - process.env.APP_ENVIRONMENT === 'lambda' - ? { rejectUnauthorized: true } - : undefined, - extra: { - connectionLimit: 10, - connectTimeout: 10000, - }, - }; -}; - -// Test-specific configuration -export const testTypeOrmConfig = (): TypeOrmModuleOptions => { - const config = databaseConfig(); - - return { - type: 'mysql', - host: config.host, - port: config.port, - username: config.username, - password: config.password, - database: config.schema || config.database, // Use test schema as database name in MySQL entities: DATABASE_ENTITIES, - synchronize: false, // Disable synchronization for better performance + synchronize: false, + migrationsRun: false, logging: false, - namingStrategy: new SnakeNamingStrategy(), - migrations: [], // Don't run migrations in test mode + dropSchema: false, + ssl: false, + retryAttempts: 1, + retryDelay: 500, extra: { - connectionLimit: 3, // Reduced connection limit for tests - connectTimeout: 5000, // Faster timeout - acquireTimeout: 3000, // Faster acquire timeout - timeout: 3000, // Query timeout - charset: 'utf8mb4_unicode_ci', - // MySQL-specific optimizations for tests - ssl: false, + connectionLimit: 2, + connectTimeout: 2000, + enableKeepAlive: true, + keepAliveInitialDelay: 0, }, - // Additional performance optimizations - cache: false, // Disable caching in tests - dropSchema: false, // Don't drop schema - migrationsRun: false, // Don't run migrations }; -}; + + if (isTestEnv) { + return { + ...baseConfig, + cache: false, + extra: { + ...baseConfig.extra, + acquireTimeout: 2000, + charset: 'utf8mb4_unicode_ci', + ssl: false, + timeout: 2000, + }, + }; + } + + return baseConfig; +} diff --git a/tests/config/test-database.module.ts b/tests/config/test-database.module.ts deleted file mode 100644 index 2f31cf9..0000000 --- a/tests/config/test-database.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { testTypeOrmConfig } from '@/config/typeorm.config'; - -@Module({ - imports: [TypeOrmModule.forRoot(testTypeOrmConfig())], - exports: [TypeOrmModule], -}) -export class TestDatabaseModule {}