Search code examples
aws-lambdanestjsserverless

The injected services are undefined using serverless


the error I have is similar to this: Dependencies not being injected on NestJS with Serverless and AWS, but the solution they give is already configured in my project, when I run the normal app, with npm run start:.. it works correctly, but when running it with serverless, I have the failure of the undefined in the services in the constructor, here I leave a summary of the configuration and code:

libs\utils\utils.module.ts

import { Module } from '@nestjs/common';
import { UtilsService } from './utils.service';
import { UtilsController } from './utils.controller';

@Module({
  providers: [UtilsService],
  exports: [UtilsService],
  controllers: [UtilsController]
})
export class UtilsModule {}

libs\utils\utils.service.ts

import {
  Injectable,
} from '@nestjs/common';

@Injectable()
export class UtilsService {
  getHello(): string {
    return 'Hello World!';
  }
}

mcr\seguros-estado\src\seguros-estado.module.ts

import { Module } from '@nestjs/common';
import { SegurosEstadoService } from './seguros-estado.service';
import { SegurosEstadoController } from './seguros-estado.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CitiesDane } from '../../../libs/entities/CitiesDane';
import { InsuranceConfigurations } from '../../../libs/entities/InsuranceConfigurations';
import { Fasecolda } from '../../../libs/entities/Fasecolda';
import { UtilsModule } from '../../../libs/utils/utils.module';
import { LoggerModule } from '../../../libs/logger/logger.module';
import { ErrorsService } from '../../../libs/errors/errors.service';
import * as dotenv from 'dotenv';

dotenv.config();
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql', // Tipo de base de datos, por ejemplo, 'mysql', 'mariadb', 'postgres', etc.
      host: process.env.DB_HOST, // Dirección del host
      port: parseInt(process.env.DB_PORT, 10), // Puerto de la base de datos
      username: process.env.DB_USER, // Usuario de la base de datos
      password: process.env.DB_PW, // Contraseña del usuario de la base de datos
      database: process.env.DB_NAME, // Nombre de la base de datos
      entities: [__dirname + '/../../../libs/entities/**/*{.ts,.js}'], // Ruta a las entidades
      synchronize: process.env.DB_SYNC === "true", // Solo en desarrollo; sincroniza la estructura de la base de datos con las entidades
      autoLoadEntities: true,
      logging: ['query', 'error'],
    }),
    TypeOrmModule.forFeature([CitiesDane]),
    TypeOrmModule.forFeature([InsuranceConfigurations]),
    TypeOrmModule.forFeature([Fasecolda]),
    UtilsModule,
    LoggerModule,
    ErrorsService,
  ],
  providers: [SegurosEstadoService],
  controllers: [SegurosEstadoController],
})
export class SegurosEstadoModule {}

mcr\seguros-estado\src\seguros-estado.controller.ts

import { Body, Controller, Get, Inject, Post } from '@nestjs/common';
import { LoggerService } from '../../../libs/logger/logger.service';
import { UtilsService } from '../../../libs/utils/utils.service';
import * as path from 'path';
import { SegurosEstadoService } from './seguros-estado.service';
const pdf2base64 = require('pdf-to-base64');
const fse = require('fs-extra');

@Controller("seguros-estado")
export class SegurosEstadoController {
  constructor(
    private utilService: UtilsService,
    private estadoService: SegurosEstadoService,
    private loggerService: LoggerService,
  ) {
    console.log("🚀 ~ SegurosEstadoController ~ utilService:", utilService) // undefined
    console.log("🚀 ~ SegurosEstadoController ~ loggerService:", loggerService) // undefined
    console.log("🚀 ~ SegurosEstadoController ~ estadoService:", estadoService) // undefined
    }
  
  @Get('test-r') 
  async testR() {
    console.log(this.utilService.getHello()); // cannon read prop getHello to undefined
    return { succesfully: true}
  }

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    //"allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "paths": {
      "libs/*": ["libs/*"]
    }
  },
  "include": ["src/**/*.ts", "mcr/**/*.ts", "libs/**/*.ts"],
}

serverless.yml

service: nestjs-microservices

frameworkVersion: '4'

provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1
  memorySize: 128
  timeout: 240
  environment:
    NODE_ENV: production
    REDIS_HOST: <redis-host>
    REDIS_PORT: <redis-port>

functions:
  segurosEstado:
    handler: mcr/seguros-estado/src/main.handler
    events:
      - http:
          path: seguros-estado/{proxy+}
          method: ANY
          cors: true

  allianz:
    handler: mcr/allianz/src/main.handler
    events:
      - http:
          path: allianz/{proxy+}
          method: ANY
          cors: true

plugins:
  - serverless-offline

custom:
  serverless-offline:
    httpPort: 3001
    debug: true

mcr\seguros-estado\src\main.ts

import { NestFactory } from '@nestjs/core';
import serverlessExpress from '@vendia/serverless-express';
import { Handler } from 'aws-lambda';
import { SegurosEstadoModule } from './seguros-estado.module';

let cachedHandler: Handler;

export const handler: Handler = async (event, context) => {
  if (!cachedHandler) {
    const app = await NestFactory.create(SegurosEstadoModule);
    await app.init();

    // Obtén la instancia de Express para serverless-express
    const expressApp = app.getHttpAdapter().getInstance();

    // Configura el adaptador serverless-express
    cachedHandler = serverlessExpress({ app: expressApp });
  }

  return cachedHandler(event, context);
};

// Si no estás en un entorno de Lambda, puedes correrlo como un servidor HTTP local
if (process.env.IS_LOCAL === 'true') {
  const bootstrap = async () => {
    const app = await NestFactory.create(SegurosEstadoModule);
    const port = process.env.SEGUROS_ESTADO_PORT || 3001;  // Usar el puerto desde el .env
    await app.listen(port);
    console.log(`Microservicio Seguros Estado corriendo en http://localhost:${port}`);
  };

  bootstrap();
}

Now when I run the app with "ts-node-dev mcr/secure-state/src/main.ts" it works correctly, but with "npx serverless offline":

Server ready: http://localhost:3001 �


ANY /dev/seguros-estado/seguros-estado/test-r (λ: segurosEstado)
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [NestFactory] Starting Nest application...
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [NestFactory] Starting Nest application... +7ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] TypeOrmModule dependencies initialized +503ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] LoggerModule dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] ErrorsService dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] LoggerModule dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] ErrorsService dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] UtilsModule dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:25 p. m.     LOG [InstanceLoader] UtilsModule dependencies initialized +0ms
query: SELECT VERSION() AS `version`
query: SELECT VERSION() AS `version`
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +1656ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
� ~ SegurosEstadoController ~ utilService: undefined
� ~ SegurosEstadoController ~ loggerService: undefined
� ~ SegurosEstadoController ~ estadoService: undefined
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [InstanceLoader] SegurosEstadoModule dependencies initialized +1ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [RoutesResolver] SegurosEstadoController {/seguros-estado}: +15ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [RouterExplorer] Mapped {/seguros-estado/test-r, GET} route +9ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [RouterExplorer] Mapped {/seguros-estado/poliza-r, POST} route +1ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [RouterExplorer] Mapped {/seguros-estado/print-r, POST} route +1ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [RoutesResolver] UtilsController {/utils}: +1ms
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.     LOG [NestApplication] Nest application successfully started +5ms
Microservicio Seguros Estado corriendo en http://localhost:3001
[Nest] 6640  - 08/01/2025, 2:04:27 p. m.   ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'getHello')
(λ: segurosEstado) RequestId: 04af350e-244b-4dbf-9113-9c4c9476cda3  Duration: 17758.24 ms  Billed Duration: 17759 ms

Solution

  • Finally the solution was to add the decorators @Inject(--service_name--) in the injections in the constructor:

    @Inject(UtilsService) private readonly utilService: UtilsService,
        @Inject(SegurosEstadoService) private readonly estadoService: SegurosEstadoService,
        @Inject(LoggerService) private readonly loggerService: LoggerService,