How can I use service in a helper class in nest.js? - node.js

I import the redis module in app.module.ts file:
#Module({
imports: [
TypegooseModule.forRoot('mongodb://mongo:27017/test', {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
}),
RedisModule.register({ url: 'redis://sessions' }),
UsersModule,
AuthModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
I would like to create a helper class SessionManager that would manage user's sessions (store, update, delete them in Redis), e.g something like this:
import { RedisService } from 'nestjs-redis';
import { User } from 'src/users/schemas/user.schema';
import { IUserSessions } from '../interfaces/Session.interface';
class SessionManager {
constructor(private redisService: RedisService) {}
saveUserSession(user: User, userAgent: string) {}
static getSessionsByUserId(userId: string): IUserSessions {
const client = this.redisService.getClient()
const sessions = client.hmgetall(userId)
return sessions
}
}
export default SessionManager
and use it in my auth.service.ts. How do I do that? Does it have to be represented as some Nest.js entity (module or provider)?

Ended up creating a separate sessions module, adding my service as a Provider and exporting it, then using it in my desired module:
import { Module } from '#nestjs/common';
import { SessionsService } from './sessions.service';
#Module({
providers: [SessionsService],
exports: [SessionsService]
})
export class SessionsModule {}

Related

How do I rename mongoose connections in NestJs

I am having trouble giving a mongoose database connection a name (so that I can use more than one).
I have separated my database config out into a separate module so that I can import it easily when testing e2e.
My Database Module looks like:
import { Module } from "#nestjs/common";
import { MongooseModule } from "#nestjs/mongoose";
import { DatabaseService } from "./database.service";
#Module({
imports: [
MongooseModule.forRootAsync({
//connectionName: 'database',
useFactory: () => ({ uri: `mongodb://mongoUri` }),
}),
],
providers: [DatabaseService],
exports: [DatabaseService]
})
export class DatabaseModule { };
and my Database Service is:
import { Injectable } from "#nestjs/common";
import { InjectConnection } from "#nestjs/mongoose";
import { Connection } from 'mongoose';
#Injectable()
export class DatabaseService {
constructor(#InjectConnection() private readonly connection: Connection) {}
getDbHandle(): Connection {
return this.connection
}
}
with App Module:
import { Module, NestModule } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MyModule } from './my/my.module';
import { DatabaseModule } from './common/database/database.module';
#Module({
imports: [
MyModule,
DatabaseModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {}
and My Module:
import { Module } from '#nestjs/common';
import { MyService } from '../services/my.service';
import { MyController } from '../controllers/my.controller';
import { MongooseModule } from '#nestjs/mongoose';
import { My } from '../models/my.schema';
#Module({
imports: [
MongooseModule.forFeature([{ name: My.name, schema: MySchema }]) //, 'database'),
],
controllers: [MyController],
providers: [MyService]
})
export class MyModule {}
This all works fine, however if I uncomment the comments (i.e. add a connection name in Database Module and to the import in My Module, I get the error: Nest can't resolve dependencies of the DatabaseService (?). Please make sure that the argument DatabaseConnection at index [0] is available in the DatabaseModule context.
What is it that adding a connection name does to cause this error? And how can I resolve it? Any help greatly appreciated.
Because #InjectConnection() takes an optional parameter which is the name, otherwise some "default" connection will be used.
See the source code here:
export const InjectConnection = (name?: string) =>
Inject(getConnectionToken(name));
https://github.com/nestjs/mongoose/blob/master/lib/common/mongoose.decorators.ts

Set role for all TypeORM connections through NestJS

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() {
// ...
}
}

Nest can't resolve dependencies of the ~repository

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,
) {}
...
}

NestJS -- JWT Dependency is not being loaded

I do not need complete passport, I just need to sign a JWT token.
I tried working with this repository
I tried all possible combinations, but I just cant integrate it in the project. I had followed the course of several different errors. I fix one, and the another pops up. So, I am including minimum part, and the current error that is thrown.
AuthModule:
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { AuthService } from './auth.service';
import { JwtService } from '#nestjs/jwt';
#Module({
imports: [ JwtModule.register({ secret: 'hard!to-guess_secret' })],
providers: [AuthService],
exports: [AuthService, JwtService]
})
export class AuthModule {}
AuthService:
import { Injectable } from '#nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '#nestjs/jwt';
#Injectable()
export class AuthService {
constructor(
private readonly jwtService: JwtService,
) {}
async signPayload (user: any) {
const payload = { username: 'HARDCORE', color:'RED' };
return {
whatever: this.jwtService.sign(payload),
};
}
}
AppModule:
#Module({
imports: [
ConfigModule.forRoot(),
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: 'wefwefwef',
}),
inject: [ConfigService],
}),
TypeOrmModule.forRoot({
type: 'mysql',
...
}),
UsersModule,
SubscriptionsModule,
ProductsModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
}
SomeController:
export class UsersController {
constructor(private readonly usersService: UsersService,
private readonly authService: AuthService
) {}
...
#ApiResponse({ status: 401, description: 'Unauthorized.' })
#ApiResponse({ status: 404, description: 'User Not Found.' })
#Get('users')
async findOne(#Query() userGetDto: UserGetDto): Promise<User> {
const user = await this.usersService.findByUsername(userGetDto.userName);
if (!user) throw new NotFoundException('User Not Found')
let signedUser = this.authService.signPayload(user);
return user;
And this is the error with this setup that I get:
Nest can't resolve dependencies of the JwtService (?). Please make
sure that the argument JWT_MODULE_OPTIONS at index [0] is available in
the JwtService context.
I spend lot of time on this one, but I just cant make it work.
Based on your error, JwtService is in an imports array somewhere. Providers (classes marked with #Injectable()) never go in the imports array, but rather should be added to the exports array and that module where the exports was added should be put in the consuming module's imports array.
Also, if you are working with a Dynamic Module (any module that uses register or forRoot or an async variant of the two) you should always export the module instead of its services, as the module most likely has important configurations necessary for the service to work.

How do I import a global provider into another global provider in NestJS?

I want to import the config provider from the documentation into another provider.
I am using the same config structure that is sown in the documentation here.
So the config.module.ts looks like this:
import { Module, Global } from '#nestjs/common';
import { ConfigService } from './config.service';
#Global()
#Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(
`config/env/${process.env.NODE_ENV || 'development'}.env`,
),
},
],
exports: [ConfigService],
})
export class ConfigModule {}
And the other provider should look something like this correct?
token.module.ts
import { Module, Global } from '#nestjs/common';
import { TokenService} from './token.service';
import { ConfigService } from './config.service';
#Global()
#Module({
import: [ConfigService]
providers: [TokenService],
exports: [TokenService],
})
export class TokenModule {}
While the TokenService should look something like this
token.service.ts
import { ConfigService } from '../../config/config.service';
export class TokenService {
constructor(private readonly configService: ConfigService) {}
test(): string {
return this.configService.get('TestVal');
}
}
But when I import TokenModule in AppModule I get this error The "path" argument must be one of type string, Buffer, or URL. Received type undefined
You forgot to mark your TokenService as #Injectable(). You need to tell NestJS about arguments that need to be provided to other services.

Resources