admin管理员组

文章数量:1406913

I'm trying to write a custom validation pipe to validate a request with multiple parameters. And I want to validate one param with a custom pipe, but another param is also needed to be used in the validation, and I didn't find the way described in the document.

For example, this is my API, it requires a chainId and an address as input parameters. I need to validate that the address is valid, but I can't do this without chainId.

Here's my code, I wrote the pipe followed by the document about custom pipe examples:

Controller

@Put('/:chainId/tokens/:address')
@ApiOperation({
  summary: 'Create a token',
})
async create(
  @Param('chainId') chainId: number,
  @Param('address', new AddressValidationPipe()) address: string,
): Promise<Token> {
  return await this.tokensService.save(chainId, address);
}

Validation pipe

@Injectable()
export class AddressValidationPipe implements PipeTransform {
  async transform(address: string) {
    // chainId should be another param, but I don't know how to get it in this pipe
    const chainId = 4;

    if (!validator(chainId, address)) {
      throw new BadRequestException(
        'Address is not valid, or it is not on this chain',
      );
    }
    return address;
  }
}

I'm trying to write a custom validation pipe to validate a request with multiple parameters. And I want to validate one param with a custom pipe, but another param is also needed to be used in the validation, and I didn't find the way described in the document.

For example, this is my API, it requires a chainId and an address as input parameters. I need to validate that the address is valid, but I can't do this without chainId.

Here's my code, I wrote the pipe followed by the document about custom pipe examples:

Controller

@Put('/:chainId/tokens/:address')
@ApiOperation({
  summary: 'Create a token',
})
async create(
  @Param('chainId') chainId: number,
  @Param('address', new AddressValidationPipe()) address: string,
): Promise<Token> {
  return await this.tokensService.save(chainId, address);
}

Validation pipe

@Injectable()
export class AddressValidationPipe implements PipeTransform {
  async transform(address: string) {
    // chainId should be another param, but I don't know how to get it in this pipe
    const chainId = 4;

    if (!validator(chainId, address)) {
      throw new BadRequestException(
        'Address is not valid, or it is not on this chain',
      );
    }
    return address;
  }
}
Share Improve this question asked Apr 13, 2022 at 2:37 Remi GuanRemi Guan 22.4k17 gold badges67 silver badges89 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

After some research, I found that I can have these two params into this pipe in one time, and only validate the address.

Validation Pipe

@Injectable()
export class AddressValidationPipe implements PipeTransform {
  constructor(private readonly configService: ConfigService) {}
  async transform(value: { chainId: string; address: string }) {
    const { chainId, address } = value;

    if (!validator(Number(chainId), address)) {
      throw new BadRequestException(
        'Address is not valid, or it is not on this chain',
      );
    }

    return { chainId: Number(chainId), address };
  }
}

Although in this way, I must convert the chainId to number by myself, and I need to tell Swagger what params I'm using because I'm not writing them as each argument in my controller.

Controller

@ApiParam({
  name: 'address',
  description: 'Address of the token',
  type: String,
})
@ApiParam({
  name: 'chainId',
  description: 'Chain ID of the token',
  type: Number,
})
@Put('/:chainId/tokens/:address')
@ApiOperation({
  summary: 'Create a token',
})
async create(
  @Param(AddressValidationPipe) param: { chainId: number; address: string },
): Promise<Token> {
  const { chainId, address } = param;
  return await this.tokensService.save(chainId, address);
}

For those who have happened into this query and answer, you can use the type like this if you would need a more constrained version of it in the future:

pipe.ts

@Injectable()
export class AddressValidationPipe
  implements
    PipeTransform<
      { chainId: string; address: string },
      { chainId: number; address: string }
    >
{
  constructor(private readonly configService: ConfigService) {}

  async transform(value: { chainId: string; address: string }): {
    chainId: number;
    address: string;
  } {
    const { chainId, address } = value;

    if (!validator(Number(chainId), address)) {
      throw new BadRequestException(
        "Address is not valid, or it is not on this chain"
      );
    }

    return { chainId: Number(chainId), address };
  }
}

controller.ts

async create(
  @Param(AddressValidationPipe) param: { chainId: string; address: string } & { chainId: number;  address: string }, // This will be in shape (params) & (return type of the pipe)
): Promise<Token> {
  const { chainId, address } = param;
  return await this.tokensService.save(chainId, address);
}

With this kind of structure, if you want to build a custom pipe with different input and output type, it will be useful as well.

本文标签: javascriptAccess multipe parameters with a custom pipe in NestjsStack Overflow