how to load a nestjs module only if network is available - nestjs

I'm working on a telegram bot (using nestjs-telegraf). I noticed that my app fails to start loading telegraf module if network connection is absent.
[Nest] 544032 - 08/06/2022, 10:01:43 LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 544032 - 08/06/2022, 10:01:43 ERROR [ExceptionHandler] request to https://api.telegram.org/bot5386725551:[REDACTED]/getMe failed, reason: getaddrinfo EAI_AGAIN api.telegram.org
FetchError: request to https://api.telegram.org/bot5386725551:[REDACTED]/getMe failed, reason: getaddrinfo EAI_AGAIN api.telegram.org
When this error occur I have to manually kill the process (CTRL-C) check the net and restart the nest's app.
How can I check if net is available before import nestjs's module or manage/intercept the EAI_AGAIN error?
this is the module that should run nestjs-telegraf that fails on net absence
import { EventModule } from './../event/event.module';
import { UsersModule } from 'src/users/users.module';
import { TowerModule } from './../tower/tower.module';
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { TelegrafModule } from 'nestjs-telegraf';
import { BotService } from './bot.service';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TelegrafModule.forRoot({ token: process.env.BOT_TOKEN }),
TowerModule,
UsersModule,
EventModule,
],
providers: [BotService],
exports: [BotService],
})
export class BotModule {}
Thanks

Related

Problem using NestJs with TypeOrm to connect in Sybase database

I am getting an error when trying to open Sybase Database connection with TypeOrm. Requirements for connection are absolutely correct (url,port,db,username,pass).
I can connect using Sybase JConnect with DB viewer. But I couldn't do that with typeorm.
TypeOrm seems to support SAP but I couldn't understand the problem.
Hana is a cloud db service but I am trying to connect to a local sybase.
This is my app.module.
import { MiddlewareConsumer, Module, NestModule } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { TypeOrmModule } from '#nestjs/typeorm';
import { AbcModule } from './abc/abc.module';
import { ApiMiddleware } from './middleware/api.middleware';
#Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
logging: true,
type: 'sap',
host: process.env.ASE_URL,
port: Number(process.env.ASE_PORT),
username: process.env.ASE_USER,
password: process.env.ASE_PASS,
database: process.env.ASE_DB,
entities: []
}),
AbcModule,
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(ApiMiddleware).forRoutes('/api');
}
}
This is connection error.
query failed: SELECT CURRENT_SCHEMA AS "schema_name" FROM "SYS"."DUMMY"
error: Error: Request timeout. Request info: (id: 8, creation time: 1668070673834)
at Request._fireTimeout (D:\Codes\WebProjects\abc-nest-api\node_modules\hdb-pool\lib\Request.js:114:17)
at Timeout.<anonymous> (D:\Codes\WebProjects\abc-nest-api\node_modules\hdb-pool\lib\Request.js:92:43)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7)

Why can't I mapped more api in NestJS?

To add http://localhost:8080/api/v1/admin (GET) to develop an api with NestJS, we added Get annotation and proceeded with the api development, but the api was not mapped, and if you call that api, a 404notfound error will occur.
What kind of problem?
// admin.controller.ts
import { Body, Controller, Get, HttpCode, Post, UseGuards } from '#nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
} from '#nestjs/swagger';
import { ReqAdmin } from 'src/lib/decorator/req-admin.decorator';
import { HttpAuthGuard } from 'src/guards/auth/http-auth.guard';
import { BaseResponse } from 'src/models/http/base.response';
import { AdminService } from './admin.service';
import { CreateAdminDto } from './dto/create.admin.dto';
import { LoginAdminDto } from './dto/login.admin.dto';
import { Admin } from 'src/entities/admin.entity';
#Controller('admin')
#ApiTags('관리자 API')
export class AdminController {
constructor(
private readonly adminService: AdminService
) {}
#Get('/')
#UseGuards(HttpAuthGuard)
#ApiOperation({ summary: '내 정보 보기', description: '내정보 보기' })
#ApiResponse({ status: 200, description: '내 정보 조회 성공' })
public async findOne(#ReqAdmin() admin: Admin): Promise<BaseResponse> {
const data = await this.adminService.findOneById(admin.id);
return BaseResponse.object('내정보 조회 성공', {
data,
})
}
#Post('/')
#ApiOperation({ summary: '관리자 생성 API', description: '관리자를 생성한다.' })
#ApiResponse({ status: 201, description: '관리자 생성성공' })
public async create(#Body() createAdminDto: CreateAdminDto) {
await this.adminService.create(createAdminDto);
return BaseResponse.object('관리자 생성성공')
}
#Post('/login')
#HttpCode(200)
#ApiOperation({ summary: '관리자 로그인 API', description: '관리자 로그인' })
#ApiResponse({ status: 200, description: '관리자 로그인성공', type: String })
public async login(#Body() loginAdminDto: LoginAdminDto) {
const token = await this.adminService.login(loginAdminDto);
return BaseResponse.object('관리자 로그인성공', {
'x-access-token': token
})
}
}
// admin.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Admin } from 'src/entities/admin.entity';
import { TokenModule } from 'src/token/token.module';
import { TokenService } from 'src/token/token.service';
import { AdminController } from './admin.controller';
import { AdminRepository } from './admin.repository';
import { AdminService } from './admin.service';
#Module({
imports: [
TypeOrmModule.forFeature([
Admin
])
],
providers: [AdminService, TokenService],
exports: [AdminService],
controllers: [AdminController]
})
export class AdminModule {}
// terminal
iwonje#iwonje-ui-MacBookAir mcn-admin-backend % nest start
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [NestFactory] Starting Nest
application...
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [InstanceLoader] AppModule
dependencies initialized +42ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [InstanceLoader] TypeOrmModule
dependencies initialized +0ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [InstanceLoader] TypeOrmCoreModule
dependencies initialized +303ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [InstanceLoader] TypeOrmModule
dependencies initialized +0ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [InstanceLoader] AdminModule
dependencies initialized +1ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [RoutesResolver] AdminController
{/api/v1/admin}: +287ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [RouterExplorer] Mapped
{/api/v1/admin, POST} route +1ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [RouterExplorer] Mapped
{/api/v1/admin/login, POST} route +1ms
[Nest] 40115 - 2022. 10. 11. 오후 4:12:15 LOG [NestApplication] Nest application
successfully started +1ms
// app.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule, TypeOrmModuleOptions } from '#nestjs/typeorm';
import { DatabaseTypes } from '../database/database.enum';
import { DatabaseFactory } from '../database/database.factory';
import { AdminModule } from 'src/admin/admin.module';
#Module({
imports: [
TypeOrmModule.forRoot(
DatabaseFactory.createDatabase(DatabaseTypes.MYSQL).options(),
),
AdminModule
],
})
export class AppModule {}
Look like you had added a prefix for your api /api/v1
You can try to call http://localhost:8080/api/v1/admin instead of http://localhost:8080/admin

Nest can't resolve dependencies of the SequelizeCoreModule

I'm trying to import SequelizeModule in my app.module.ts but I got the following error:
[Nest] ERROR [ExceptionHandler] Nest can't resolve dependencies of the
SequelizeCoreModule (SequelizeModuleOptions, ?). Please make sure that
the argument ModuleRef at index [1] is available in the
SequelizeCoreModule context.
app.module.ts
import { Module } from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { SequelizeModule } from '#nestjs/sequelize';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TweetsController } from './tweets/tweets.controller';
import { TweetsModule } from './tweets/tweets.module';
import { TweetsService } from './tweets/tweets.service';
#Module({
imports: [
SequelizeModule.forRoot({
dialect: 'sqlite',
autoLoadModels: true,
synchronize: true,
host: join(__dirname, 'database.sqlite'),
}),
TweetsModule,
],
controllers: [AppController, TweetsController],
providers: [AppService, TweetsService],
})
export class AppModule {}
this happens when you have multiple nodejs modules loaded for the same #nestjs/core package. See them by running npm ls #nestjs/core. You can solve that by getting ride of those packages somehow and keeping only the one that your app depends on directly. Read the docs: https://docs.nestjs.com/faq/common-errors#cannot-resolve-dependency-error

NestJs - ConfigModule.forRoot isGlobal not working

I am trying to load the "process.env.AUTH_SECRET" in AuthModule, but it is giving me the undefined error "Error: secretOrPrivateKey must have a value".
I did setup the "isGlobal: true" in AppModule and it was able to read "process.env.MONGO_URL" there fine.
I have also installed dotenv, which reads fine if I do:
import * as dotenv from 'dotenv';
dotenv.config();
export const jwtConstants = {
secret: process.env.AUTH_SECRET,
};
But I would rather do it the "NestJs" way as the doc says adding isGlobal should make the env available to all other modules.
auth.module.ts
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { UserModule } from '../user/user.module';
import { PassportModule } from '#nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtModule } from '#nestjs/jwt';
#Module({
imports: [
UserModule,
PassportModule,
JwtModule.register({
secret: process.env.AUTH_SECRET, //Cannot read this.
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule } from '#nestjs/config';
import { AuthModule } from './auth/auth.module';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
UserModule,
MongooseModule.forRoot(process.env.MONGO_URL), // Can read this fine
AuthModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
What am I missing or doing wrong? Just for reference, I am trying to follow this authentication tutorial.
Thank you,
Your MongooseModule and AuthModule are dependent on your ConfigModule, but they don't know it.
Your modules are busy being loaded up, however your ConfigModule has to preform an async i/o process (reading your .env) before it can be ready. Meanwhile, Nest carries on loading without waiting for ConfigModule to finish it's job unless another it finds another module is dependent on one of it's exports.
This is where a modules *Async flavoured methods come in to play. They give you control over the modules instantiation. In this context, they allow us to inject the ConfigService from the ConfigModule which will not happen until the ConfigService is ready.
So changing your JWTModule configuration from using .register to .registerAsync to inject the ConfigService is what you are after:
JWTModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => {
secret: config.get<string>('AUTH_SECRET'),
signOptions: { expiresIn: '60s' }
}
})
Now, JWTModule will not load until ConfigService is ready and available in scope. You will most likely need to do that for your MongooseModule too.
That is the "NestJS" way.
Saying that, if all you really needed to do was have your .env loaded into process.env, put:
import * as dotenv from 'dotenv';
import { resolve } from 'path';
dotenv.config({ path: resolve(__dirname, '../.env') });
At the start of your main.ts file. That would lead to dotenv synchronously loading it before anything else happened

Nest can't resolve dependencies of the searchService (?). Please make sure that the argument at index

I am using Nestjs framework to develop my Elastic Service application.
I am using '#nestjs/elasticsearch' library inside my code and i am simply trying to establish database connection and use inside all other module. Please find my code example here.
My App Module looks below
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';
import { DatabaseModule } from './database/database.module';
import { LayoutmgmtModule } from './layoutmgmt/layoutmgmt.module';
#Module({
imports: [ConfigModule,DatabaseModule, LayoutmgmtModule],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}
My Database Module is,
import { Module } from '#nestjs/common';
import { ElasticsearchModule } from '#nestjs/elasticsearch';
import {ConfigModule} from '../config/config.module';
import {ConfigService} from '../config/config.service';
import {DatabaseService} from './database.service';
#Module({
imports:[ElasticsearchModule.registerAsync({
imports:[ConfigModule],
useFactory: async (configService: ConfigService) => ({
host: configService.get('ELASTIC_URL'),
log: 'trace',
requestTimeout: 3000
}),
inject:[ConfigService]
})],
providers:[DatabaseService],
})
export class DatabaseModule {}
My Database Service is,
import { Injectable,HttpException } from '#nestjs/common';
import { ElasticsearchService } from '#nestjs/elasticsearch';
import { Client } from 'elasticsearch';
#Injectable()
export class DatabaseService {
private readonly esClient:Client;
constructor(private readonly elasticsearchService: ElasticsearchService) {
try {
this.esClient = elasticsearchService.getClient();
this.esClient.ping({ requestTimeout: 3000 },function(err,res,status){
if (err || !(res)) {
console.log('Unable to connect to the server. Please start the server. Error:', err);
throw new HttpException({
status: 'error',
message: 'Unable to connect to the server. Please start the server. Error:'
}, 500);
} else {
console.log('Connected to Server successfully!',res, status);
}
});
}
catch(err) {
console.log('Error in connection' + err);
throw new HttpException({
status: 'error',
message: 'Unable to reach Elasticsearch cluster'
}, 500);
}
}
}
Now Above i had initialized the connection and its getting connected to the database without issues, But i am trying to re-use ElasticsearchService in another module/service called layout module
Layout Module looks below
import { Module } from '#nestjs/common';
import { LayoutmgmtController } from './layoutmgmt.controller';
import { LayoutmgmtService } from './layoutmgmt.service';
#Module({
controllers: [LayoutmgmtController],
providers: [LayoutmgmtService],
})
export class LayoutmgmtModule {}
Layout Service Looks below
import { Inject, Injectable, Dependencies } from '#nestjs/common';
import { ElasticsearchService } from '#nestjs/elasticsearch';
import { Client } from 'elasticsearch';
#Injectable()
export class LayoutmgmtService {
private readonly esClient:Client;
constructor(#Inject(ElasticsearchService) private readonly elasticsearchService: ElasticsearchService) {
this.esClient = elasticsearchService.getClient();
if (!this.esClient){
console.log("Elastic alreayd connected")
}
}
}
If i use the ElasticSErachService in above service inside the constructor i am getting the below error, I wanted to reuse the existing connection ..
[Nest] 10724 - 10/14/2019, 4:50:41 PM [ExceptionHandler] Nest can't resolve dependencies of the LayoutmgmtService (?). Please make sure that the argument at index [0] is available in the LayoutmgmtModule context. +40ms
Error: Nest can't resolve dependencies of the LayoutmgmtService (?). Please make sure that the argument at index [0] is available in the LayoutmgmtModule context.
at Injector.lookupComponentInExports (C:\Subu\Elastic\elastic-nest-js\node_modules#nestjs\core\injector\injector.js:183:19)
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:744:11)
at Object. (C:\Subu\Elastic\elastic-nest-js\node_modules\ts-node\src\bin.ts:158:12)
at Module._compile (internal/modules/cjs/loader.js:688:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
at Module.load (internal/modules/cjs/loader.js:598:32)
at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
at Function.Module._load (internal/modules/cjs/loader.js:529:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
LayoutmgmtModule and DatabaseModule are not related anyway in your code.
You have registered the ElasticsearchModule in DatabaseModule but not in LayoutmgmtModule so it is unable to find the service.
Solution 1
You can get rid of the LayoutmgmtModule by just adding LayoutmgmtController and LayoutmgmtService in DataBaseModule and it should start working
Solution 2
You can make DataBaseModule as global by just adding #Global() before #Module decorator as mentioned here
You are not exporting ElasticsearchService anywhere. Perhaps your DatabaseModule should export it together with DatabaseService (LayoutmgmtService should use either of those).
On top of that, you should add given Service to providers of LayoutmgmtModule

Resources