Skip to content

Commit

Permalink
feat(core,common): add @RawBody() decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
tolgap committed Feb 18, 2024
1 parent 033f181 commit c3e2d5b
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 1 deletion.
47 changes: 47 additions & 0 deletions packages/common/decorators/http/route-params.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,53 @@ export function Body(
);
}

/**
* Route handler parameter decorator. Extracts the `rawBody` Buffer
* property from the `req` object and populates the decorated parameter with that value.
*
* For example:
* ```typescript
* async create(@RawBody() rawBody: Buffer | undefined)
* ```
*
* @see [Request object](https://docs.nestjs.com/controllers#request-object)
* @see [Raw body](https://docs.nestjs.com/faq/raw-body)
*
* @publicApi
*/
export function RawBody(): ParameterDecorator;

/**
* Route handler parameter decorator. Extracts the `rawBody` Buffer
* property from the `req` object and populates the decorated parameter with that value.
* Also applies pipes to the bound rawBody parameter.
*
* For example:
* ```typescript
* async create(@RawBody(new ValidationPipe()) rawBody: Buffer)
* ```
*
* @param pipes one or more pipes - either instances or classes - to apply to
* the bound body parameter.
*
* @see [Request object](https://docs.nestjs.com/controllers#request-object)
* @see [Raw body](https://docs.nestjs.com/faq/raw-body)
* @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes)
*
* @publicApi
*/
export function RawBody(
...pipes: (
| Type<PipeTransform<Buffer | undefined>>
| PipeTransform<Buffer | undefined>
)[]
): ParameterDecorator {
return createPipesRouteParamDecorator(RouteParamtypes.RAW_BODY)(
undefined,
...pipes,
);
}

/**
* Route handler parameter decorator. Extracts the `params`
* property from the `req` object and populates the decorated
Expand Down
1 change: 1 addition & 0 deletions packages/common/enums/route-paramtypes.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum RouteParamtypes {
RESPONSE,
NEXT,
BODY,
RAW_BODY,
QUERY,
PARAM,
HEADERS,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/router/route-params-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export class RouteParamsFactory implements IRouteParamsFactory {
return res as any;
case RouteParamtypes.BODY:
return data && req.body ? req.body[data] : req.body;
case RouteParamtypes.RAW_BODY:
return req.rawBody;
case RouteParamtypes.PARAM:
return data ? req.params[data] : req.params;
case RouteParamtypes.HOST:
Expand Down
1 change: 1 addition & 0 deletions packages/core/router/router-execution-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export class RouterExecutionContext {
public isPipeable(type: number | string): boolean {
return (
type === RouteParamtypes.BODY ||
type === RouteParamtypes.RAW_BODY ||
type === RouteParamtypes.QUERY ||
type === RouteParamtypes.PARAM ||
type === RouteParamtypes.FILE ||
Expand Down
11 changes: 11 additions & 0 deletions packages/core/test/router/route-params-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('RouteParamsFactory', () => {
const req = {
ip: 'ip',
session: null,
rawBody: Buffer.from('{"foo":"bar"}'),
body: {
foo: 'bar',
},
Expand Down Expand Up @@ -67,6 +68,16 @@ describe('RouteParamsFactory', () => {
).to.be.eql(req.body);
});
});
describe(`RouteParamtypes.RAW_BODY`, () => {
it('should return rawBody buffer', () => {
expect(
(factory as any).exchangeKeyForValue(
RouteParamtypes.RAW_BODY,
...args,
),
).to.be.eql(req.rawBody);
});
});
describe(`RouteParamtypes.HEADERS`, () => {
it('should return headers object', () => {
expect(
Expand Down
16 changes: 15 additions & 1 deletion packages/core/test/router/router-execution-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('RouterExecutionContext', () => {
beforeEach(() => {
consumerApplySpy = sinon.spy(consumer, 'apply');
});
describe('when paramtype is query, body or param', () => {
describe('when paramtype is query, body, rawBody or param', () => {
it('should call "consumer.apply" with expected arguments', () => {
contextCreator.getParamValue(
value,
Expand Down Expand Up @@ -238,6 +238,19 @@ describe('RouterExecutionContext', () => {
),
).to.be.true;

contextCreator.getParamValue(
value,
{ metatype, type: RouteParamtypes.RAW_BODY, data: null },
transforms,
);
expect(
consumerApplySpy.calledWith(
value,
{ metatype, type: RouteParamtypes.RAW_BODY, data: null },
transforms,
),
).to.be.true;

contextCreator.getParamValue(
value,
{ metatype, type: RouteParamtypes.PARAM, data: null },
Expand All @@ -261,6 +274,7 @@ describe('RouterExecutionContext', () => {
});
it('otherwise', () => {
expect(contextCreator.isPipeable(RouteParamtypes.BODY)).to.be.true;
expect(contextCreator.isPipeable(RouteParamtypes.RAW_BODY)).to.be.true;
expect(contextCreator.isPipeable(RouteParamtypes.QUERY)).to.be.true;
expect(contextCreator.isPipeable(RouteParamtypes.PARAM)).to.be.true;
expect(contextCreator.isPipeable(RouteParamtypes.FILE)).to.be.true;
Expand Down

0 comments on commit c3e2d5b

Please sign in to comment.