NestJS documentation describes how to define multiple databases:
https://docs.nestjs.com/techniques/database#multiple-databases
As well as using a database config service for asynchronous configuration:
https://docs.nestjs.com/techniques/database#async-configuration
Is it possible to define multiple databases from an async config service?
Experimentally I tried returning an array of options from my service:
class TypeOrmConfigService implements TypeOrmOptionsFactory {
createTypeOrmOptions() {
return [
{ ... },
{ ... }
];
}
}
but no luck with this approach.
It looks like for each connection you want to have you will need to create a separate configuration instance. What I might suggest would be some sort of factory that takes in a name, and maybe the config key for the database connection, then using those in the factory to help return the expected values. So the docs have a factory like
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: 'mysql',
host: configService.getString('HOST'),
port: configService.getString('PORT'),
username: configService.getString('USERNAME'),
password: configService.getString('PASSWORD'),
database: configService.getString('DATABASE'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
inject: [ConfigService],
});
You could create a similar factory along the lines of
const typeOrmFactory = (
configName: string,
entities: string[],
databaseName?: string
) => (configService: ConfigService): TypeOrmModuleOptions => ({
type: 'postgres', // whatever you use
url: configService.get(configName), // dynamically change which connection you're working with based on the factory input
name: databaseName || 'default',
synchronize: true,
entities // path to the entity/entities you are working with
});
Now in your app.module you can use the factory like so
#Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: typeOrmFactory('DATABASE_URL', ['some/path.ts']),
inject: [ConfigService]
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: typeOrmFactory('DATABASE_URL_2', ['some/other/path.ts'], 'secondaryDatabase'),
inject: [ConfigServce]
],
})
export class AppModule {}
Make sure you tell Typescript that you are returning the type TypeOrmModuleOptions otherwise it tells you that the functions are not compatible. You can also save most of the configs in the config service and environment variables if you would like.
Related
I started building a simple telegram bot using nestjs-telegraf package.
Following the docs, I created the basic AppUpdate service:
#Update()
#Injectable()
export class AppUpdate {
#Command('random')
async onRandom(#Ctx() ctx: Context) {
const resp = await asyncRequestToGetData(ctx.message.text);
await ctx.reply(resp);
}
}
And provided it like this in app.module
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TelegrafModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
token: configService.get<string>('TELEGRAM_BOT_TOKEN'),
}),
inject: [ConfigService],
}),
HttpModule,
],
controllers: [AppController],
providers: [AppUpdate, ...],
})
export class AppModule {}
The asyncRequestToGetData is an http call that sometimes takes 10 seconds or more.
And for some reason if another user tries to call the same command on the bot while request is processing, the onRandom is not called until the processing is finished.
Basically, not able to handle concurrent requests.
Was not able to find the reason/solution so far.
We have a web server running Nest 8.0.8. With the given module setup:
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
validate,
load: [configuration]
}),
SequelizeModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => config.get('db1'),
inject: [ConfigService]
}),
SequelizeModule.forRootAsync({
name: 'db2',
imports: [ConfigModule],
useFactory: (config: ConfigService) => config.get('db2'),
inject: [ConfigService],
}),
controllers: [...],
providers: [...]
})
To start the application, we have this main.ts file:
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(...)
);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (errors) => new ValidationException(errors),
transform: true,
whitelist: true,
validationError: {
target: false,
value: false
}
})
);
app.setGlobalPrefix('v1');
app.useWebSocketAdapter(new RedisIoAdapter(app));
app.enableCors({ origin: '*' });
/**
* When SIGTERM, run functions before shutting down
*/
app.enableShutdownHooks();
if (process.env.SETUP) {
const service = app.get(FooService);
await service.setup();
await app.close();
return;
}
await app.listen(3000, '0.0.0.0');
}
bootstrap().then();
When running out unit tests, we use Nest to automatically configure the database. Initially after initializing the server with the command SETUP=true nest start that will force Nest to not listen the port and early return after running service.setup(). (note that this problem also happens when calling the app.listen(...) and app.close() functions)
The problem happens at the call app.close() we expect Nest to close all connections, including the two SequelizeModule of the 2 databases connected.
Unfortunaly Nest closes the first connection and tries to close the second, but the module (we suspect) is already closed. Giving the error:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error: Nest could not find Sequelize element (this provider does not exist in the current context)
at InstanceLinksHost.get (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/instance-links-host.js:21:19)
at Object.find (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/module-ref.js:38:55)
at Object.get (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/module.js:345:28)
at SequelizeCoreModule.<anonymous> (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:81:47)
at Generator.next (<anonymous>)
at /Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:20:71
at new Promise (<anonymous>)
at __awaiter (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:16:12)
at SequelizeCoreModule.onApplicationShutdown (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:80:16)
at Object.callAppShutdownHook (/Users/me/Repository/project_nest/node_modules/#nestjs/core/hooks/on-app-shutdown.hook.js:51:35)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at NestApplication.callShutdownHook (/Users/me/Repository/project_nest/node_modules/#nestjs/core/nest-application-context.js:199:13)
at NestApplication.close (/Users/me/Repository/project_nest/node_modules/#nestjs/core/nest-application-context.js:83:9)
at bootstrap (/Users/me/Repository/project_nest/src/main.ts:143:5)
We initially thing that is a problem on Nest but we are not sure. This used to work in the past but somehow after a update the issue started happening and we cannot downgrade the library due some constraints with other dependencies.
I would like to know what can be done to either debug or sort the issue, we tried to debug the application but nothing points out to issues in our codebase. Could you please help?
Regards, Pedro
you need to give name for you second configuration SequelizeModule.
Example for your situation
SequelizeModule.forRootAsync({
name: 'db2',
imports: [ConfigModule],
useFactory: (config: ConfigService) => {
name: 'db2',
...config.get('db2')
},
inject: [ConfigService],
}),
The reason of this problem is getConnectionToken function which uses for getting connection by name from options. And if you not use specific name, SequelizeModule will use one connection between two modules and will close them both
I am working on a NestJs project using Bull queue.
Here is the code I am using to connect Redis, and it works well。
BullModule.registerQueueAsync(
{
name: 'test',
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
redis: {
host: configService.get('REDIS_ADDR'),
port: configService.get('REDIS_PORT'),
},
}),
inject: [ConfigService],
}
)
Now, I need to switch to using Redis Sentinel.
I searched online, but I could not find an appropriate tutorial.
I appreciate any help you can provide.
I have one module (auth module) where I register JwtModule:
JwtModule.register({
secret: process.env.SECRET_KEY,
signOptions: { expiresIn: '60m' },
}),
Register function looks like this: static register(options: JwtModuleOptions): DynamicModule;
JwtModule is defined in node_modules (jwt.module.js)
let JwtModule = JwtModule_1 = class JwtModule {
static register(options) {
console.log(options);
console.log(process.env.SECRET_KEY);
return {
module: JwtModule_1,
providers: jwt_providers_1.createJwtProvider(options)
};
}
In app.module.ts I have
ConfigModule.forRoot({
isGlobal: true,
}),
process.env.SECRET_KEY is undefined in node_modules file (console.log(process.env.SECRET_KEY)), but signOptions: { expiresIn: '60m' } is defined.
If I try to write to console SECRET_KEY from .env everywhere else is defined, but in node_modules is undefined.
The ConfigModule's ConfigService runs dotenv's config() method, but all decorators run at the same time, so the config() method is not called yet. You can use the async registration instead and use the ConfigService like so
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get('SECRET_KEY')
})
})
Be sure that you Call the ConfigService in your App Module constructor :
export class AppModule {
constructor(private configService: ConfigService) {}
}
I want to make a provider which queries an entity from db. (what interests me actually is to make typeorm available when I create a provider).
I get
[ExceptionHandler] Connection "default" was not found. - {"trace":"ConnectionNotFoundError: Connection \"default\" was not found.\n
I tried using service, using custom repository, nothing works. I have something like this in module:
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: []
}
TypeOrm is imported on app.module.ts
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => (configService.get('database'))
}),
Any help is appreciated.
Check your database file. The Entity may not load as expected.
For newest NestJS version. We should load from ./dist/
Fixed by injecting Connection from typeorm.
import { getRepository, Connection } from 'typeorm';
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: [Connection]
}