The nestjs class-validator does not work.
For example, if I send a post request with a number for LoginId, I get a normal response.
import { IsNumber, IsString } from 'class-validator';
export class LoginUserDto {
#IsString()
loginId: string;
#IsString()
password: string;
}
class-validator needs to work with Pipe.
You can refer to the following code to inject APP_PIPE or see the nestjs/pipe doc.
import { Module, ValidationPipe } from '#nestjs/common';
import { APP_PIPE } from '#nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_PIPE,
useValue: new ValidationPipe({
whitelist: true,
}),
},
],
})
export class AppModule {}
Class Validator should work I am Using it for a long time.
I think in your case it treats LoginId(number, your input) as a string
Try Class Transformer
import { IsNumber, IsString, IsNotEmpty() } from 'class-validator';
import { Type } from 'class-transformer';
export class LoginUserDto {
#IsNotEmpty()
#IsString()
#Type(()=>String)
loginId: string;
#IsString()
#IsNotEmpty()
password: string;
}
Related
I am trying to use jsonwebtoken with NestJs, and have been battling with this issue whereby NestJS returns an empty object once it encouters my code line to generate a token.
Each time, I try to generate a token, the server simply responds with an empty object.
Here is my jwt strategy code for passport
import { ConfigService } from "#nestjs/config";
import { ExtractJwt, Strategy } from "passport-jwt";
import { PassportStrategy } from "#nestjs/passport";
import { Inject, Injectable, UnauthorizedException } from "#nestjs/common";
import { RidersService } from "../riders/riders.service";
import { DriversService } from "../drivers/drivers.service";
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly riderService: RidersService,
private readonly driverService: DriversService,
#Inject(ConfigService) config: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: config.get("JWT_SECRET"),
});
}
async validate(payload: {
role: string;
iat: number;
exp: number;
phone: string;
}) {
if (!payload) {
throw new UnauthorizedException();
}
const service = {
rider: this.riderService,
driver: this.driverService,
};
const user = await service[payload.role].findByAny(payload.phone);
if (!user) {
throw new UnauthorizedException();
}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
const { iat, exp, ...rest } = payload;
return rest;
}
}
Now here is my AuthService stripped off
import { InjectModel } from "#nestjs/mongoose";
import { Injectable, Logger, HttpException } from "#nestjs/common";
import { JwtService } from "#nestjs/jwt";
#Injectable()
export class AuthService {
private readonly logger = new Logger(AuthService.name);
constructor(
private readonly jwtService: JwtService,
) {}
async register(
phone: string,
role: string,
): Promise<LoginResponseDTO | GENERIC_RESPONSE | unknown> {
return {
token: this.jwtService.sign({ role, phone }),
};
}
}
This line return { token: this.jwtService.sign({ role, phone }), } returns an empt object {} no matter what I do, once the code execution gets there, it stops and sends back an empty response.
Here is my Authmodule stripped off
import { JwtStrategy } from "./jwt.strategy";
import { Module } from "#nestjs/common";
import { AuthService } from "./auth.service";
import { AuthController } from "./auth.controller";
import { PassportModule } from "#nestjs/passport";
import { JwtModule } from "#nestjs/jwt";
import { ConfigModule, ConfigService } from "#nestjs/config";
#Module({
imports: [
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>("JWT_SECRET"),
signOptions: { expiresIn: configService.get<string>("JWT_EXPIRESIN") },
}),
inject: [ConfigService],
}),
],
providers: [
AuthService,
JwtStrategy,
],
exports: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
Here is my Authcontroller stripped off
import { AuthService } from "./auth.service";
import { BadRequestException, Controller, Post, Query } from "#nestjs/common";
import { ParsePhonePipe } from "./../pipes/transform-phone.pipes";
#Controller("auth")
export class AuthController {
constructor(private readonly authService: AuthService) {}
#Post("signup")
async register(
#Query("phone", new ParsePhonePipe()) phone: string,
#Query("role") role: string,
) {
if (!role) {
throw new BadRequestException("user role is missing");
}
try {
const response = await this.authService.register(phone, role);
console.log(response, "RESPONSE");
return response;
} catch (error) {
return error;
}
}
}
Here is Appmodule stripped of
import { ConfigModule, ConfigService } from "#nestjs/config";
import { Module } from "#nestjs/common";
import { APP_GUARD } from "#nestjs/core";
import { AuthModule } from "./auth/auth.module";
import { JwtAuthGuard } from "./auth/jwt.auth.guard";
#Module({
imports: [
AuthModule,
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>("RYDR_DB_URI"),
}),
inject: [ConfigService],
}),
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
TripHistoryService,
DriversService,
],
})
export class AppModule {}
The login response has nothing to do with the jwt as there are three possible response types defined.
I am unable to generate jwt token and it simply exit with an empty object {}, there is no error whatsoever.
What might I be doing wrong?
I tried to use jsonwebtoken package itself, expecting that there might be an issue with NestJs but it still failed to generate any token instead it returned an empty object as a response.
I want to use configService inside a DTO
my app.module.ts is
...
Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env', '.env.dev', '.env.stage', '.env.prod'], //if a variable is found in multiple files, the first one takes precedence.
load: [
databaseConfig,
authConfig,
appConfig,
mailConfig,
],
}),
...
And I want to use configService in my DTO like:
import { ApiProperty } from '#nestjs/swagger';
import { MaxLength, IsNotEmpty, IsEmail, IsString } from 'class-validator';
import { ConfigService } from '#nestjs/config';
let configService: ConfigService;
export class LoginDto {
#IsEmail()
#ApiProperty({ example: configService.get('auth.dummyEmail') })
readonly email: string;
#IsNotEmpty()
#IsString()
#MaxLength(60)
#ApiProperty({ example: 'secret' })
readonly password: string;
}
I get this error:
#ApiProperty({ example: configService.get('auth.dummyEmail') })
import { ApiProperty } from '#nestjs/swagger';
import { MaxLength, IsNotEmpty, IsEmail, IsString } from 'class-validator';
import { ConfigService } from '#nestjs/config';
import appConfig from '../../config/app.config';
const configService = new ConfigService({ app: appConfig() });
export class LoginDto {
#IsEmail()
#ApiProperty({ example: configService.get('app.auth.dummyEmail') })
readonly email: string;
#IsNotEmpty()
#IsString()
#MaxLength(60)
#ApiProperty({ example: 'secret' })
readonly password: string;
}
i'm facing an issue with NestJS :
"[Nest] 5068 - 08/11/2021, 3:12:02 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the AppService (?). Please make sure that the argument UserRepository at index [0] is available in the AppModule context."
I tried to add AppService in the imports with no luck.
How to evade this error?
app.module.ts :
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { User } from './user.entity';
#Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'test',
entities: [User],
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
app.controller.ts :
import { Body, Controller, Get, Post } from '#nestjs/common';
import { AppService } from './app.service';
import * as bcrypt from 'bcrypt';
#Controller('api')
export class AppController {
constructor(private readonly appService: AppService) {
}
#Post('register')
async register(
#Body('name') name: string,
#Body('email') email: string,
#Body('password') password: string,
#Body('phone') phone: string,
) {
const hashedPassword = await bcrypt.hash(password, 12);
return this.appService.create({
name,
email,
password: hashedPassword,
phone,
})
}
}
app.service.ts :
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
#Injectable()
export class AppService {
constructor(
#InjectRepository(User) private readonly userRepository: Repository<User>
) {
}
async create(data: any): Promise<User> {
return this.userRepository.save(data)
}
}
user.entiry.ts :
import { Column, Entity, PrimaryColumn } from "typeorm";
#Entity('users')
export class User {
#PrimaryColumn()
id: number;
#Column()
name: string;
#Column()
email: string;
#Column()
phone: string;
#Column()
password: string;
}
Thank you
You need to add TypeormModule.forFeature([User]) to your imports in your AppModule to set up the #InjectRepository(User) that you make use of. With the TypeORM module, forRoot/Async is for database connection and general TypeORM configuration, forFeature is for dynamic provider setup
I am a newbie in NestJs world. As far as I know, I imported everything needed in the JwtStrategy. I don't know where it went wrong. Can somebody help me with this?
As far as I referred to documetation, Whenever we want to use any entity in a module, we should import that entity in the imports field in the #Module() decorator. I did it.
jwt.strategy.ts
import { Injectable, UnauthorizedException } from "#nestjs/common";
import { PassportStrategy } from "#nestjs/passport";
import { Strategy, ExtractJwt } from "passport-jwt";
import { InjectRepository } from "#nestjs/typeorm";
import { Repository } from "typeorm";
import { UserEntity } from "src/entities/user.entity";
import { AuthPayload } from "src/common/dtos/user.dto";
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
#InjectRepository(UserEntity)
private userRepo: Repository<UserEntity>
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.SECRETKEY
});
}
async validate(payload: AuthPayload): Promise<UserEntity> {
const { username } = payload;
const user = this.userRepo.findOne({ where: { username: username } });
if(!user) {
throw new UnauthorizedException();
}
return user;
}
}
auth.module.ts
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '#nestjs/typeorm';
import { UserEntity } from 'src/entities/user.entity';
import { JwtModule } from '#nestjs/jwt';
import { PassportModule } from '#nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
#Module({
imports: [
TypeOrmModule.forFeature([UserEntity]),
JwtModule.register({
secret: process.env.SECRETKEY,
}),
PassportModule.register({
defaultStrategy: 'jwt'
})
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [PassportModule, JwtStrategy]
})
export class AuthModule {}
user.entity.ts
import { Entity, Column, OneToMany, JoinTable, BeforeInsert } from "typeorm";
import { AbstractEntity } from "./abstract-entity.abstract";
import { IsEmail } from "class-validator";
import { Exclude, classToPlain } from "class-transformer";
import * as bcrypt from "bcryptjs";
import { CategoryEntity } from "./category.entity";
import { ArticleEntity } from "./article.entity";
#Entity('User')
export class UserEntity extends AbstractEntity {
#Column({
type: "varchar",
length: 80
})
fullName: string;
#Column({
type: "varchar",
unique: true
})
#IsEmail()
email: string;
#Column({
type: "varchar",
unique: true
})
username: string;
#Column({
type: "varchar"
})
#Exclude()
password: string;
#Column({
default: null,
nullable: true
})
avatar: string | null;
#Column({
type: "varchar",
unique: true
})
phoneNumber: string;
#Column({
type: "boolean",
default: false
})
isAdmin: boolean;
#Column({
type: "boolean",
default: false
})
isStaff: boolean;
#Column({
type: "boolean",
default: false
})
isEmailVerified: boolean;
#OneToMany(type => CategoryEntity, category => category.createdBy)
#JoinTable()
categories: CategoryEntity[];
#OneToMany(type => ArticleEntity, article => article.createdBy)
#JoinTable()
articles: ArticleEntity[];
#BeforeInsert()
async hashPassword() {
this.password = await bcrypt.hash(this.password, 10);
}
async comparePassword(attempt: string): Promise<boolean> {
return await bcrypt.compare(attempt, this.password);
}
toJSON(): any {
return classToPlain(this);
}
}
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from "#nestjs/typeorm";
import { APP_FILTER, APP_INTERCEPTOR } from '#nestjs/core';
import {
DatabaseConnectionService
} from "./utils/database-connection.service";
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ArticlesModule } from './articles/articles.module';
import { HttpExceptionFilter } from './common/exception-filters/http-exception.filter';
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
import { CategoryModule } from './category/category.module';
#Module({
imports: [
TypeOrmModule.forRootAsync({
useClass: DatabaseConnectionService
}),
AuthModule,
UsersModule,
ArticlesModule,
CategoryModule,
],
controllers: [AppController],
providers: [
// {
// provide: APP_INTERCEPTOR,
// useClass: ResponseInterceptor
// },
{
provide: APP_FILTER,
useClass: HttpExceptionFilter
},
AppService
],
})
export class AppModule {}
database-connection.service.ts
import { Injectable } from "#nestjs/common";
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from "#nestjs/typeorm";
import { truncate } from "fs";
#Injectable()
export class DatabaseConnectionService implements TypeOrmOptionsFactory {
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: "mysql",
host: process.env.HOST,
port: parseInt(process.env.PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DATABASE,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
dropSchema: true,
autoLoadEntities: true,
logger: "simple-console"
};
}
}
The Error is as follows:
Based on your error, somewhere you have JwtStrategy in your imports array. If you need the JwtStrategy you should instead import the AuthModule, as providers should only be in the providers array and should never be in imports.
Consider to move to Active Record pattern. All what you need to do is just to let your AbstractEntity extends BaseEntity of TypeOrm.
You can remove all typeorm features imports like:
TypeOrmModule.forFeature([UserEntity])
and all dependency injections for repository like:
#InjectRepository(UserEntity)
private userRepo: Repository<UserEntity>
Just use the entity class for querying:
const user = await UserEntity.findOne({ where: { username } });
I'm new to nest and I have run into an error which is not really decodable for me. I tried to follow the nest example, but no luck.
[tsl] ERROR in /Users/user/Documents/development/sprinta/src/server/app.module.ts(12,5)
[0] TS2322: Type '(DynamicModule | typeof ProjectModule)[]' is not assignable to type 'Type<any> | DynamicModule | Promise<DynamicModule> | ForwardReference<any>'.
[0] Type '(DynamicModule | typeof ProjectModule)[]' is not assignable to type 'ForwardReference<any>'.
[0] Property 'forwardRef' is missing in type '(DynamicModule | typeof ProjectModule)[]'.
app.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { AngularUniversalModule } from './modules/angular-universal/angular-universal.module';
import { JiraService } from './services/jira.service'
// modules
import { ProjectModule } from './resources/local/projects/project.module'
// sync
import {ProjectsSync} from './sync/projects.sync'
#Module({
imports: [
[TypeOrmModule.forRoot(), ProjectModule],
AngularUniversalModule.forRoot(),
],
controllers: [],
providers:[JiraService, ProjectsSync],
})
export class ApplicationModule {}
project.module.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { ProjectService } from './project.service';
import { ProjectController } from './project.controller';
import { Project } from './project.entity';
#Module({
imports: [TypeOrmModule.forFeature([Project])],
providers: [ProjectService],
controllers: [ProjectController],
})
export class ProjectModule {}
project.service.ts
import { Injectable, Inject } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Project } from './project.entity';
#Injectable()
export class ProjectService {
constructor(
#InjectRepository(Project)
private readonly projectRepository: Repository<Project>,
) {}
async findAll(): Promise<Project[]> {
return await this.projectRepository.find();
}
}
project.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class Project {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 500 })
name: string;
}
project.controller.ts
import { Controller, Get } from '#nestjs/common';
import { ProjectService } from './project.service';
import { Project } from './project.entity';
#Controller('project')
export class ProjectController {
constructor(private readonly projectService: ProjectService) {}
#Get()
findAll(): Promise<Project[]> {
return this.projectService.findAll();
}
}
Thanks for the help
You have an extra nested array around TypeOrmModule.forRoot() and ProjectModule. The #Module call should read:
#Module({
imports: [
TypeOrmModule.forRoot(), ProjectModule,
AngularUniversalModule.forRoot(),
],
controllers: [],
providers:[JiraService, ProjectsSync],
})