NestJS — Creating custom parameter decorators for your APIs with createParamDecorator()
If you have played with NestJS for a while, you should have been familiar with decorators. It is because NestJS utilized decorators to do the declaration, configuration and assignment of your objects. You should have seen them everywhere.

Here are five decorators that you may already have been frequently using for creating APIs: @Session, @Param, @Body , @Query and @Headers , which access req.session , req.params , req.body , req.query and req.headers correspondingly.
@Post('create-proposal')
async createProposal(
@Body('title') title,
@Body('price') price,
...
) {
...
}However, what if you want to access your custom request attributes, such as req.jwt ?
@Post('create-proposal')
async createProposal(
@Jwt('memberId') memberId,
@Body('title') title,
@Body('price') price,
...
) {
...
}Custom parameter decorators with a key
Actually, NestJS already has a built-in function for you to create such custom decorators — createParamDecorator()
import { createParamDecorator } from '@nestjs/common';
export const Jwt = createParamDecorator((data, req) => {
return req.jwt;
});Very simple, isn’t?
In default, the decorator created will accept 1 optional parameter, which is data . One common way to utilize it is to put the attribute key there.
import { createParamDecorator } from '@nestjs/common';export const Jwt = createParamDecorator((data, req) => {
let jwt = req['jwt'];
return jwt && data ? jwt[data] : jwt;
});As a result, if you put @Jwt() , it will assign the whole req.jwt object to your variable. If there is a key provided, such as @Jwt('memberId') , it will assign req.jwt.memberId instead.
In fact, you can find a similar section at the official document. Hence, I am going to add something that you cannot find from that document in the next section.
Custom parameter decorators with multiple options
The problem here is, having a key can solve most use cases, but what if only a key is not enough? Suppose you want to add validation in your custom parameter decorator — if req.jwt is null, it will throw an exception. However, you want some of the APIs to be able to bypass the validation.
@Post('create-proposal')
async createProposal(
@Jwt('memberId') memberId,
@Body('title') title,
@Body('price') price,
...
) {
...
}@Get('get-proposal')
async getProposal(
@Jwt('memberId', { isNullable: true }) memberId,
@Query('proposalId') proposalId
) {
...
}The simplest way is to create two different decorators — @Jwt and @JwtNullable, one with validation, one without. It will work perfectly fine, except it is a bit ugly. Also, it will be problematic when you have multiple options.
Another simple way is to wrap the key and all your options into a single object. It is slightly better than the previous one, but still not ideal as you need to wrap the key into an object even if there isn’t any options.
@Post('create-proposal')
async createProposal(
@Jwt({ key: 'memberId' }) memberId,
@Body('title') title,
@Body('price') price,
...
) {
...
}@Get('get-proposal')
async getProposal(
@Jwt({ key: 'memberId', isNullable: true }) memberId,
@Query('proposalId') proposalId
) {
...
}My solution is actually simpler than you may think. The key point here is that, decorators are in fact, just functions. What you need to do is to create a wrapper function in between.
import { createParamDecorator } from '@nestjs/common';
import { HttpException } from '@nestjs/common/exceptions'export const Jwt = (key, options: {
isNullable?: boolean
} = {}): ParameterDecorator => {
let validators = []; if (!options.isNullable) {
validators.push(v => {
if (!v) {
throw new HttpException({
statusCode: 401,
error: "Unauthorized",
message: "Cannot find a valid JWT token"
}, 401);
}
})
} return createParamDecorator((data, req) => {
let { key, validators } = data;
let jwt = req['jwt'];
validators.forEach(validator => { validator(jwt) })
return jwt && key ? jwt[key] : jwt;
})({ key, validators });
}You are creating a function which accepts two parameters — key and options. Instead of directly using the decorator created with createParamDecorator() in your controller, you are now using your custom function as decorator, which helps you to transform your parameters into a single object. As a result, you can encapsulate the ugly part and keep the decorator clean.
When to use createParamDecorator()?
I have written another article about creating decorators from scratch for API validation previously and I didn’t use createParamDecorator() there. Simply speaking, you should use createParamDecorator() only if you are creating parameter decorators, and your decorator is interacting with the req object. If your decorator has nothing to do with the API request, createParamDecorator() will not help.






