I have a NestJS project I have set up and am trying to use Sequelizer to access my database. Currently, I have a database provider and module set up to be used globally (the module is injected in my app.module) that are set up as follows:
database.module.ts:
import {Global, Module } from '#nestjs/common';
import { databaseProvider } from './database.provider'
#Global()
#Module({
providers: [databaseProvider],
exports: [databaseProvider]
})
export class DatabaseModule{}
database.provider.ts:
import {Sequelize } from 'sequelize-typescript';
export const databaseProvider = {
provide: 'SequelizeInstance',
useFactory: async () => {
const config = {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
logging: false,
dialect: 'mssql',
force: false,
timezone: '-07:00'
};
const sequelize = new Sequelize(config);
return sequelize;
}
};
My module to call the code uses a module and service that is setup as follows:
db.module.ts:
import { Module } from '#nestjs/common';
import { DBService } from './db.service';
import { DBController } from './db.controller'
#Module({
controllers: [DBController],
providers: [DBService],
exports: [DBService]
})
export class DBModule {}
db.service.ts:
import { Inject, Injectable } from '#nestjs/common';
#Injectable()
export class DBService {
constructor(
#Inject('SequelizeInstance') private readonly sequelizeInstance
) {}
public async getRegions(): Promise<any> {
return this.sequelizeInstance.query('EXEC pr_region_lst')
.then(data => data)
.catch(error => console.log(error));
}
}
The problem I am having is that sequelizer keeps throwing an error that the Login failed for user ''. I attached a debugger and am looking at the code in the db.service.ts. When I look at sequelizeInstance, it shows the config having the username from the configuration file I provided. I don't know why it is not using it in the DB context though.
I've resolved my issue. It seems that the 'tedious' package was not installed on my project. The msssql package however was. So it was apparently not using the driver correctly to connect to the database. Once I added tedious, the connection started working.
Related
I have a Nest JS application using TypeORM 0.3 version. The application is not starting with the new DataSource configuration.
Error: Nest JS application is not starting with Data source new typeORM 0.3 way.
ERROR MESSAGE:
Blocked in below error. Not sure how to fix it.
Nest can't resolve dependencies of the TypeOrmModuleOptions (?). Please make sure that the argument dependency at index [0] is available in the TypeOrmCoreModule context.
Potential solutions:
If dependency is a provider, is it part of the current TypeOrmCoreModule?
If dependency is exported from a separate #Module, is that module imported within TypeOrmCoreModule?
Code:
src/db/data-source.ts
This contains TypeORM 0.3 Data source connection file
```
import {
TypeOrmModuleAsyncOptions,
TypeOrmModuleOptions,
} from '#nestjs/typeorm';
// import { Tag } from '#app/tag/tag.entity';
import { DataSource, DataSourceOptions } from 'typeorm';
export const dataSourceOptions: DataSourceOptions = {
migrationsTableName: 'migrations',
type: 'postgres',
host: process.env.HOST,
port: +process.env.PORT,
username: process.env.USERNAME,
password: process.env.PASSWORD,
database: process.env.DATABASE,
migrations: [__dirname + '\\migrations\\**\\*{.ts,.js}'],
// entities: [Tag],
// entities: [__dirname + '/**/*.entity{.ts,.js}'],
// migrations: [__dirname + '/../database/migrations/*{.ts,.js}'],
// cli: {
// migrationsDir: __dirname + '/../database/migrations',
// },
// extra: {
// charset: 'utf8mb4_unicode_ci',
// },
logging: true,
synchronize: false,
};
const dataSource = new DataSource(dataSourceOptions);
export default dataSource;
```
In Nest JS App module file imported Data source and added to imports as suggested in official documentation
Any help will be appreciated team.
Screenshots: Code and Error
Code: commit
src/app.module.ts
```
import { forwardRef, Module } from '#nestjs/common';
import { dataSourceOptions } from '#app/db/data-source';
import { AppController } from '#app/app.controller';
import { AppService } from '#app/app.service';
import { TagModule } from '#app/tag/tag.module';
import { TypeOrmModule } from '#nestjs/typeorm';
import { ConfigModule, ConfigService } from '#nestjs/config';
// import { Tag } from './tag/tag.entity';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync(dataSourceOptions),
// forwardRef(() => TagModule),
// TagModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```
I am using TypeORM by way of NestJS to connect to a PostgreSQL database. For every connection I make to the database, I need to 'SET ROLE my-service', preferably just once when the connection is established. What are my options?
I have tried to find a way using the config that's passed to TypeOrmModule.forRoot(). I've considered the possibility that I can issue instructions to the PostgreSQL client ahead of time, but I don't have a strong sense of how to do that.
Please scroll to Update 2, per your unpated question.
Option 1: As an injectable service
You can create a service SomeTypeOrmConfigService as an #Injectable(), and then inject it like so.
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '#nestjs/typeorm';
// create your injectable here
#Injectable()
export class SomeTypeOrmConfigService implements TypeOrmOptionsFactory {
constructor(private configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.configService.get('DATABASE_HOST'),
username: this.configService.get('DATABASE_USERNAME'),
password: this.configService.get('DATABASE_PASSWORD'),
};
}
}
Option 2: Save the information in a common .env, or b) another approach database.provider.ts file and export that copied ans./credit to from here
import { ConfigModule, ConfigService } from '#nestjs/config';
import { TypeOrmModule } from '#nestjs/typeorm';
export const databaseProviders = [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('PGHOST'),
port: +configService.get<number>('PGPORT'),
username: configService.get('PGUSER'),
password: configService.get('PGPASSWORD'),
database: configService.get('PGDATABASE'),
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: false,
logging: true,
}),
}),
];
Then import it in your database.module.ts
import { databaseProviders } from './database.provider';
import { DatabaseService } from './database.service';
import { Module } from '#nestjs/common';
#Module({
imports: [...databaseProviders],
providers: [DatabaseService],
exports: [...databaseProviders],
})
export class DatabaseModule {}
And that's all, you can add multiple connections in your database.provider.ts if you want it, also, donĀ“t forget to create the .env and import the database module in your root module.
Update Answer (V2) to updated question
In a nutshell.. I think you are missing a) session module like nestjs-session , once you use that you can optionally b) cache it in your role with your session object
Step 1: use nestjs-session
Step 2: Now you can have access/use role in the session property object of your request
Step 3: You can then make it an injectable or a user factory with the help of the NestSessionOptions
My humble recommendation, save yourself time, use a pre-rolled package as a decorator.. try this package from here - use their sample ref. below ->
npm i nestjs-roles
1. Create Your roles
// role.enum.ts
export enum Role {
ADMIN = 'ADMIN',
USER = 'USER',
MODERATOR = 'MODERATOR',
}
2. Let's say you use nestjs-session and keep role in session property object of request. So then create guard with getRole callback:
// roles.guard.ts
import { ExecutionContext } from '#nestjs/common';
import { createRolesGuard } from 'nestjs-roles';
import { Role } from './role.enum';
function getRole(context: ExecutionContext) {
const { session } = context.switchToHttp().getRequest();
if (!session) {
return;
}
return (session as { role?: Role }).role;
}
export const Roles = createRolesGuard<Role>(getRole);
3. After that we can set Roles guard globally (don't forget to pass Reflector instance):
// bootstrap.ts
import { NestFactory, Reflector } from '#nestjs/core';
import { Roles } from './roles.guard';
const app = await NestFactory.create(AppModule);
const reflector = app.get<Reflector>(Reflector);
app.useGlobalGuards(new Roles(reflector));
4. All settings are done. Now you can set up access in your controllers:
// secrets.controller.ts
import { Roles } from './roles.guard';
#Controller('secrets')
#Roles.Params(true) // setup access on Controller for users with any existing role
export class SecretsController {
#Get('my')
async readMy() {
// ...
}
#Patch(':id')
#Roles.Params(Role.ADMIN) // override access on certain handler
async update() {
// ...
}
}
I'm getting a strange error with my nestjs code.
Repository not connected in module file
[ExceptionHandler] Nest can't resolve dependencies of the UserRepository (?). Please make sure that the argument Connection at index [0] is available in the TypeOrmModule context
The current code is this
app.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import connectionOptions from 'ormconfig';
import { UserModule } from './modules';
#Module({
imports: [TypeOrmModule.forRoot(connectionOptions), UserModule],
providers: [],
})
export class AppModule {}
user.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { UserRepository } from 'src/repositories';
import { UserController } from './user.controller';
import { UserService } from './user.service';
#Module({
imports: [TypeOrmModule.forFeature([UserRepository])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
user.repository.ts
import { User } from '../entities';
import { EntityRepository, Repository } from 'typeorm';
#EntityRepository(User)
export class UserRepository extends Repository<User> {
async findOneById(userId: string) {
return await this.createQueryBuilder()
.where('user_id = :userId', {
userId,
})
.getOne();
}
}
How can I fix it?
From the code you provided, there should be no problem. The only thing that will trigger the error is the ormconfig config you did not provide.
Can you check if you have name: "xxxx" in your ormconfig config like below:
{
name: 'dbname', <-- check if you have name config here
type: 'mysql',
host: 'localhost',
port: 3306,
...
}
There are 2 ways to solve it if you have name in the config:
If there is only ONE database, you can remove the name and use the default name typeorm provides.
If you have multiple databases or you want to preserve the connection name, you will need to specify the name in the code as well to help typeorm to determine which database to work with, see the below changes:
user.module.ts
#Module({
imports: [
TypeOrmModule.forFeature([UserRepository], 'dbname'), // provide the name here
],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
user.service.ts
#Injectable()
export class UserService {
constructor(
#InjectRepository(UserRepository, 'dbname') // provide the name here
private usersRepository: UserRepository,
) {}
...
}
Connecting to two databases using TypeORM and NestJS throws a ConnectionNotFoundError when a custom repository is registered using the connection (UserConnection & PhotoConnection) one for each database. I have created a minimal repo here https://github.com/masonridge/connissuewithndb. The TypeOrm registration is done in the AppModule of NestJS
(app.module.ts)
#Module({
imports: [
TypeOrmModule.forRootAsync({
name: 'PhotoConnection',
useFactory: async () => {
return {
type: 'sqlite',
synchronize: true,
database: 'TestPhoto.sqlite',
entities: [Photo],
} as SqliteConnectionOptions;
},
}),
TypeOrmModule.forRootAsync({
name: 'UserConnection',
useFactory: async () => {
return {
type: 'sqlite',
synchronize: true,
database: 'TestUser.sqlite',
entities: [User],
} as SqliteConnectionOptions;
},
}),
// TypeOrmModule.forFeature([User, UserRepository], 'UserConnection'),
// TypeOrmModule.forFeature([Photo, PhotoRepository], 'PhotoConnection'),
PhotoModule, UserModule,
],
(photo.module.ts) DB connection1 - PhotoConnection is registered here
#Module({
imports: [
TypeOrmModule.forFeature([PhotoRepository], 'PhotoConnection'),
],
providers: [
PhotoService,
],
controllers: [PhotoController],
exports: [TypeOrmModule],
})
export class PhotoModule {}
(user.module.ts) DB connection2 - UserConnection is registered here
#Module({
imports: [
TypeOrmModule.forFeature([UserRepository], 'UserConnection'),
],
providers: [
UserService,
],
exports: [TypeOrmModule],
controllers: [UserController],
})
export class UserModule {}
(user.repository.ts) Custom repository
#EntityRepository(User)
export class UserRepository extends Repository<User> {
async createUser(name: string): Promise<string> {
const user = this.create();
user.username = name;
user.salt = 'salt';
user.password = 'xxkdkdk';
user.save();
return name;
}
}
(photo.repository.ts)
#EntityRepository(Photo)
export class PhotoRepository extends Repository<Photo> {
async createPhoto(name: string): Promise<string> {
const photo = this.create();
photo.name = name;
photo.save();
return name;
}
}
The repo is injected into the service using the connection (PhotoConnection)
(photo.service.ts)
export class PhotoService {
constructor(
#InjectRepository(Photo, 'PhotoConnection')
private readonly photoRepository: PhotoRepository,
) {}
and here using UserConnection (user.service.ts)
#Injectable()
export class UserService {
constructor(
#InjectRepository(User, 'UserConnection')
private readonly userRepository: UserRepository,
) {}
The application starts fine but on a POST request it throws a ConnectionNotFoundError error
(node:190812) UnhandledPromiseRejectionWarning: ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (C:\nestjs\type-orm-dbscratch\node_modules\typeorm\error\ConnectionNotFoundError.js:10:28)
at ConnectionManager.get (C:\nestjs\type-orm-dbscratch\node_modules\typeorm\connection\ConnectionManager.js:38:19)
at Object.getConnection (C:\nestjs\type-orm-dbscratch\node_modules\typeorm\index.js:244:35)
at Function.BaseEntity.getRepository (C:\nestjs\type-orm-dbscratch\node_modules\typeorm\repository\BaseEntity.js:67:57)
at Photo.BaseEntity.save (C:\nestjs\type-orm-dbscratch\node_modules\typeorm\repository\BaseEntity.js:27:33)
at PhotoRepository.createPhoto (C:\nestjs\type-orm-dbscratch\dist\db\photo\photo.repository.js:15:15)
at PhotoService.createPhoto (C:\nestjs\type-orm-dbscratch\dist\db\photo\photo.service.js:24:43)
at PhotoController.addSetting (C:\nestjs\type-orm-dbscratch\dist\db\photo\photo.controller.js:22:27)
at C:\nestjs\type-orm-dbscratch\node_modules#nestjs\core\router\router-execution-context.js:37:29
at process._tickCallback (internal/process/next_tick.js:68:7)
I would like to know if there is an issue with the registration. Any help would be appreciated.
It looks like the issue is between using the TypeORM approach of DataMapper and ActiveRecord. It seems using the ActiveRecord approach does not support named repositories with NestJS, and as such was looking for connection named 'default'. If you remove the extends BaseEntity and use the repository's save method instead this.save(user) vs user.save() you'll get a successful save. I didn't see an immediate way to set the connection of the ActiveRecord but it may be an option somewhere if that is still the approach you would like to follow.
I have a subscriber for NestJS to listen to any create, update or delete events (TypeORM). When one of these events is fired, I'd like to use an injected service in order to create a new revision entry.
However, it seems I cannot get the dependency loaded inside of the subscriber and the service comes back as being undefined
Key files:
EntityModificationSubscriber (Subscriber)
RevisionEntity (Entity)
app.module.ts
#Module({
imports: [
HttpModule,
TypeOrmModule.forRoot({
type: (process.env.DB_TYPE as any) || 'postgres',
host: process.env.DB_HOST || '127.0.0.1',
port: (process.env.DB_PORT as any) || 5432,
username: process.env.DB_USER || 'root',
password: process.env.DB_PASS || '',
database: process.env.DB_NAME || 'test',
entities: [join(__dirname, '**/**.entity{.ts,.js}')],
synchronize: true,
logging: 'all',
logger: 'advanced-console',
subscribers: [EntityModificationSubscriber],
}),
TypeOrmModule.forFeature([
RevisionEntity,
]),
TerminusModule.forRootAsync({
// Inject the TypeOrmHealthIndicator provided by nestjs/terminus
inject: [TypeOrmHealthIndicator, MicroserviceHealthIndicator],
useFactory: (db, msg) => getTerminusOptions(db, msg),
}),
GraphQLModule.forRoot({
debug: true,
playground: true,
typePaths: ['./**/*.graphql'],
}),
],
controllers: [AppController],
providers: [
RevisionService,
EntityModificationSubscriber,
],
})
entity_modification_subscriber.ts
import {EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent} from 'typeorm';
import {RevisionEntity, RevisonEntityStatus} from '../entities/revison.entity';
import {RevisionService} from '../services/revisions.service';
import {Injectable} from '#nestjs/common';
#Injectable()
#EventSubscriber()
export class EntityModificationSubscriber implements EntitySubscriberInterface {
constructor(private revisionService: RevisionService) {
}
// tslint:disable-next-line:no-empty
afterInsert(event: InsertEvent<any>): Promise<any> | void {
const revision = new RevisionEntity();
revision.action = RevisonEntityStatus.Created;
}
afterUpdate(event: UpdateEvent<any>): Promise<any> | void {
}
// tslint:disable-next-line:no-empty
afterRemove(event: RemoveEvent<any>) {
// this.revisionService.createRevisionEntry(revision);
}
}
The only way I found to inject a dependency into a subscriber using NestJS, was not to register that subscriber in the TypeORM configuration. I subscribe it manually into the TypeORM connection on subscriber's constructor.
import { EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent, Connection } from 'typeorm';
import { RevisionEntity, RevisonEntityStatus } from '../entities/revison.entity';
import { RevisionService } from '../services/revisions.service';
import { Injectable } from '#nestjs/common';
#Injectable()
#EventSubscriber()
export class EntityModificationSubscriber implements EntitySubscriberInterface {
constructor(private readonly connection: Connection, private readonly revisionService: RevisionService) {
connection.subscribers.push(this); // <---- THIS
}
// tslint:disable-next-line:no-empty
afterInsert(event: InsertEvent<any>): Promise<any> | void {
const revision = new RevisionEntity();
revision.action = RevisonEntityStatus.Created;
//this.revisionService <- should be working!
}
afterUpdate(event: UpdateEvent<any>): Promise<any> | void {
}
// tslint:disable-next-line:no-empty
afterRemove(event: RemoveEvent<any>) {
// this.revisionService.createRevisionEntry(revision);
}
}
Then in your app.module on the TypeORM module configuration (TypeOrmModule.forRoot).
Remove the line:
subscribers: [EntityModificationSubscriber],
It solved to me, hope it help others.
You can find discussions about that in some NestJS issues/pull requests.
https://github.com/nestjs/typeorm/issues/85
https://github.com/nestjs/typeorm/pull/27
Hi the problem is that If you want to preform database actions you would have to use:
event.manager
and Don't use getEntityManager() or getRepository() or any other global function.
This is due to a transaction. Save is running in a transaction and your data is saved in a transaction which is not committed yet. But global functions you use are running out of transaction.
more about issue here:
https://github.com/typeorm/typeorm/issues/681