export class MyDto extends Base{
#ApiModelProperty()
#Expose()
#MyCustomModifier()
readonly code: string = "";
MyCustomModifier(){
// modify
code = someUpdateOnCode()
}
Can we do something like this, so we can update dto properties
#Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private readonly schema) {}
transform(value: any, metadata: ArgumentMetadata) {
const { error } = this.schema.validate(value);
if (error) {
console.log(error, 'error');
throw new BadRequestException(error.message);
}
// some changing value.code = someUpdateOnCode()
return value;
}
}
and use your pipe like this
import * as Joi from '#hapi/joi';
#Put('')
#UsePipes(
new JoiValidationPipe(
Joi.object().keys({
code: Joi.string()
.min(3)
.max(250)
.allow('')
.optional()
)
})
async someControler(){}
Related
i did Inject Repository(User) but it did not work for me .
i want to call : this.users Repository.create
but give this error :
Type Error: this.users Repository.create is not a function
......
i did Inject Repository(User) but it did not work for me .
i want to call : this.users Repository.create
but give this error :
Type Error: this.users Repository.create is not a function
Service :
import { HttpException, HttpStatus, Inject, Injectable,forwardRef } from '#nestjs/common';
import { AuthenticationService } from 'src/authentication/authentication.service';
import { Repository } from 'typeorm';
import CreateUserDto from './dto/create-user.dto';
import { InjectRepository } from '#nestjs/typeorm';
import User from './entities/user.entity';
#Injectable()
export class UserService {
constructor(
#Inject(forwardRef(() => AuthenticationService))
// #Inject(User)
// private usersRepository: Repository<User>
#InjectRepository(User) private usersRepository: Repository<User>,
private readonly authenticationService: AuthenticationService,
) {}
async getByEmail(email: string) {
const user = await this.usersRepository.findOne({ email });
if (user) {
return user;
}
throw new HttpException('User with this email does not exist', HttpStatus.NOT_FOUND);
}
async getById(id: number) {
const user = await this.usersRepository.findOne({ id });
if (user) {
return user;
}
throw new HttpException('User with this id does not exist', HttpStatus.NOT_FOUND);
}
async create(userData: CreateUserDto) {
const newUser = await this.usersRepository.create(userData);
await this.usersRepository.save(newUser);
return newUser;
}
}
Module :
import { Module,forwardRef } from '#nestjs/common';
import { UserService } from './users.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import User from './entities/user.entity';
import { UsersController } from './users.controller';
import { AuthenticationService } from 'src/authentication/authentication.service';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { JwtModule, JwtService } from '#nestjs/jwt';
#Module({
imports: [TypeOrmModule.forFeature([User]),JwtModule.register({})],
providers: [UserService,AuthenticationService,ConfigService],
exports: [UserService,AuthenticationService,ConfigService],
controllers:[UsersController]
})
export class UsersModule {}
Entity :
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
#Entity('User')
class User {
#PrimaryGeneratedColumn()
public id?: number;
#Column({ unique: true })
public email: string;
#Column()
public name: string;
#Column()
public password: string;
}
export default User;
I found a solution by creating a custom repository and extend it with built in works fine.
Here is link :
https://clownhacker.tistory.com/250
I think it happened because I did changes with my entity class, I hope it will help someone.
In my case in this.repository, this was not pointing to correct instance. Used bind to fix it.
How to to insert current logged in user to createdBy & lastChangedBy fields after creating/updating entity?
In my BaseEntity i've tried
#BeforeInsert()
async insertUser(#GetAuthUserPayload() userPayload: User) {
const user = await this.usersService.findOne({
where: { username: userPayload.username },
});
this.createdBy = user;
this.lastChangedBy = user;
}
But i've found out decorators work only in controllers(in entity they return undefined). Is there any other way than updating DTO in controller or using session?
Since i am using #nestjsx/crud i haven't found any other method than updating DTO. I've managed to solve this issue by creating BaseService:
import { TypeOrmCrudService } from '#nestjsx/crud-typeorm';
import { InjectRepository } from '#nestjs/typeorm';
import { Inject, Injectable, Scope, Type } from '#nestjs/common';
import { CrudRequest, Override } from '#nestjsx/crud';
import { DeepPartial } from 'typeorm';
import { REQUEST } from '#nestjs/core';
import { User } from '../users/entities/user.entity';
export interface IBaseService<T> {}
type Constructor<I> = new (...args: any[]) => I;
export function BaseService<T>(entity: Constructor<T>): Type<IBaseService<T>> {
#Injectable({
scope: Scope.REQUEST,
})
class BaseServiceHost extends TypeOrmCrudService<T> implements IBaseService<T> {
constructor(#InjectRepository(entity) repo, #Inject(REQUEST) readonly request: any) {
super(repo);
}
#Override()
createOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.createOne(req, this.addCreatedByToDTO(dto));
}
#Override()
replaceOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.replaceOne(req, this.addLastChangedByToDTO(dto));
}
#Override()
updateOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.updateOne(req, this.addLastChangedByToDTO(dto));
}
private addCreatedByToDTO(dto: DeepPartial<T>): DeepPartial<T> {
const userUUID: Partial<User> = this.request.user.userUUID;
return { ...dto, createdBy: userUUID };
}
private addLastChangedByToDTO(dto: DeepPartial<T>): DeepPartial<T> {
const userUUID: Partial<User> = this.request.user.userUUID;
return { ...dto, lastChangedBy: userUUID };
}
}
return BaseServiceHost;
}
Later on i just extend my service like:
#Injectable()
export class ExampleService extends BaseService(ExampleEntity) {}
There is a nest.js project, where in the request body we expect an object, one property of this object contains stringified JSON value. The idea is to convert this string to an object, validate it and pass to controller as an object
ValidationPipe set up:
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
}),
);
DTO:
#Transform(parseJson, { toClassOnly: true })
#Type(() => AdditionalInfo)
#IsNotEmptyObject()
#ValidateNested()
additionalInfo: AdditionalInfo;
parseJson function
export function parseJson(options: {
key: string;
value: string;
obj: string | Record<string, any>;
}): Record<string, any> {
try {
return JSON.parse(options.value);
} catch (e) {
throw new BadRequestException(`${options.key} contains invalid JSON `);
}
}
For some reason in the controller the parsed value gets lost, and we receive an empty object.
Looks like #Transform works well with primitives only.
Decided to create ParseJsonPipe and use it instead.
Usage (in the controller):
#Body('additionalInfo', new ParseJsonPipe(), new ValidationPipe(AdditionalInfoDto)) additionalInfo: AdditionalInfo,
ParseJsonPipe:
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '#nestjs/common';
#Injectable()
export class ParseJsonPipe implements PipeTransform<string, Record<string, any>> {
transform(value: string, metadata: ArgumentMetadata): Record<string, any> {
const propertyName = metadata.data;
try {
return JSON.parse(value);
} catch (e) {
throw new BadRequestException(`${propertyName} contains invalid JSON `);
}
}
}
ValidationPipe implements PipeTransform from #nestjs/common, transform function looks like that:
async transform(value: any): Promise<any> {
if (!this.metaType) { // AdditionalInfoDto
return value;
}
const object = plainToClass(this.metaType, value);
const errors = await validate(object);
if (errors.length > 0) {
const message = this.getErrorMessages(errors);
throw new BadRequestException({ message });
}
return value;
}
I try to use resolver class inheritance in TypeGraphQL working with Nestjs, but don't know how to inject PostService into BaseResolver
import { ClassType } from 'type-graphql';
import { Query, Resolver, Args } from '#nestjs/graphql';
import { UseGuards } from '#nestjs/common';
import { PostService } from './post.service';
import { GqlAuthGuard } from 'src/account/auth/auth.guard';
export const createPostBaseResolver = <T extends ClassType>(
suffix: string,
objectTypeCls: T,
) => {
#Resolver(() => objectTypeCls, { isAbstract: true })
abstract class PostBaseResolver {
constructor(private readonly postService: PostService) {}
#Query(() => objectTypeCls, { name: `${suffix}` })
#UseGuards(GqlAuthGuard)
async getPostById(#Args('id') id: number) {
// here postService is undefined
return await this.postService.get(id);
}
}
return PostBaseResolver as any;
};
I want to validate body payload using class-validator in a nest.js controller. My currency.dto.ts file is like this:
import {
IsNotEmpty,
IsString,
ValidateNested,
IsNumber,
IsDefined,
} from 'class-validator';
class Data {
#IsNotEmpty()
#IsString()
type: string;
#IsNotEmpty()
#IsNumber()
id: number;
}
export class CurrencyDTO {
#ValidateNested({ each: true })
#IsDefined()
data: Data[];
}
and in my nest.js controller, I use it like this.
#Post()
#UseGuards(new AuthTokenGuard())
#UsePipes(new ValidationPipe())
addNewCurrency(#Req() req, #Body() data: CurrencyDTO) {
console.log('data', data);
}
my validation pipe class is like this:
import {
PipeTransform,
Injectable,
ArgumentMetadata,
BadRequestException,
HttpException,
HttpStatus,
} from '#nestjs/common';
import { validate, IsInstance } from 'class-validator';
import { plainToClass, Exclude } from 'class-transformer';
#Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, metadata: ArgumentMetadata) {
if (value instanceof Object && this.isEmpty(value)) {
throw new HttpException(
`Validation failed: No Body provided`,
HttpStatus.BAD_REQUEST,
);
}
const { metatype } = metadata;
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value);
const errorsList = await validate(object);
if (errorsList.length > 0) {
const errors = [];
for (const error of errorsList) {
const errorsObject = error.constraints;
const { isNotEmpty } = errorsObject;
if (isNotEmpty) {
const parameter = isNotEmpty.split(' ')[0];
errors.push({
title: `The ${parameter} parameter is required.`,
parameter: `${parameter}`,
});
}
}
if (errors.length > 0) {
throw new HttpException({ errors }, HttpStatus.BAD_REQUEST);
}
}
return value;
}
private toValidate(metatype): boolean {
const types = [String, Boolean, Number, Array, Object];
return !types.find(type => metatype === type);
}
private isEmpty(value: any) {
if (Object.keys(value).length > 0) {
return false;
}
return true;
}
}
This validation pipe works fine for all except for nested objects. Any idea what am I doing wrong here?
My body payload is like this:
{
"data": [{
"id": 1,
"type": "a"
}]
}
Try specifying the nested type with #Type:
import { Type } from 'class-transformer';
export class CurrencyDTO {
#ValidateNested({ each: true })
#Type(() => Data)
data: Data[];
}
For a nested type to be validated, it needs to be an instance of a class not just a plain data object. With the #Type decorator you tell class-transformer to instantiate a class for the given property when plainToClass is called in your VaildationPipe.
If you are using the built-in ValidationPipe make sure you have set the option transform: true.
At least in my case, the accepted answer needed some more info. As is, the validation will not run if the key data does not exist on the request. To get full validation try:
#IsDefined()
#IsNotEmptyObject()
#ValidateNested()
#Type(() => CreateOrganizationDto)
#ApiProperty()
organization: CreateOrganizationDto;