Last active
September 7, 2022 15:29
-
-
Save josephdpurcell/d4eff886786d58f58b86107c0947e19e to your computer and use it in GitHub Desktop.
PartialBody decorator for NestJS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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