Skip to content

Instantly share code, notes, and snippets.

@josephdpurcell
Last active September 7, 2022 15:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josephdpurcell/d4eff886786d58f58b86107c0947e19e to your computer and use it in GitHub Desktop.
Save josephdpurcell/d4eff886786d58f58b86107c0947e19e to your computer and use it in GitHub Desktop.
PartialBody decorator for NestJS
import { ParamDecoratorEnhancer } from '@nestjs/common';
import { METADATA__PARAM_TYPE } from './constants'; // METADATA__PARAM_TYPE = 'partialBodyType'
/**
* This enhancer grabs the Typescript type of the parameter and shoves it into
* the metadata. We do this because the CustomParamFactory does not have access
* to the type of param.
*/
export const paramTypeEnhancer: ParamDecoratorEnhancer = (
target: Record<string, unknown>,
propertyKey: string,
parameterIndex: number,
): void => {
// Typescript adds the "design:paramtypes" metadata with an array of class
// types where the keys are the method argument index and the value is the
// class type.
const paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey);
// We can use the parameterIndex to retrieve the specific type we want.
const metatype = paramTypes[parameterIndex];
// Now, we assign the parameter type to the metadata at a key we know.
Reflect.defineMetadata(METADATA__PARAM_TYPE, metatype, target[propertyKey]);
};
import { ArgumentMetadata, createParamDecorator, ExecutionContext } from '@nestjs/common';
import { METADATA__PARAM_TYPE } from './constants'; // METADATA__PARAM_TYPE = 'partialBodyType'
import { paramTypeEnhancer } from './param-type.enhancer';
/**
* Just like @Body() but allows partials. You must have
* validateCustomDecorators=false in your global validation pipe.
*/
export const PartialBody = createParamDecorator(
// Someone may want to explicitly choose the type to validate against, so if
// they pass a parameter like @PartialBody(MyType), then data will be MyType
// and we will use that for validation instead of the metadata type found.
async (data: unknown, ctx: ExecutionContext): Promise<any> => {
// Determine the type, either explicitly provided or from the metadata.
const metatype = data !== undefined ? data : Reflect.getOwnMetadata(METADATA__PARAM_TYPE, ctx.getHandler());
// Validate partially.
const request = ctx.switchToHttp().getRequest();
const transformedValue = plainToClassFromExist(metatype, request.body);
const errors = await validate(transformedValue, {
skipUndefinedProperties: true
});
// TODO: throw errors
},
[paramTypeEnhancer],
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment