@@ -12,9 +12,10 @@ import {
12
12
NestFastifyApplication ,
13
13
} from '@nestjs/platform-fastify' ;
14
14
import type { FastifyInstance , HTTPMethods , RouteOptions } from 'fastify' ;
15
+ import rawBody from 'fastify-raw-body' ;
15
16
import * as zlib from 'node:zlib' ;
16
17
import { ConfigService } from '~/core/config/config.service' ;
17
- import { RouteConfig , RouteConstraints } from './decorators' ;
18
+ import { RawBody , RouteConfig , RouteConstraints } from './decorators' ;
18
19
import type { CookieOptions , CorsOptions , IResponse } from './types' ;
19
20
20
21
export type NestHttpApplication = NestFastifyApplication & {
@@ -54,6 +55,9 @@ export class HttpAdapter extends PatchedFastifyAdapter {
54
55
} ) ;
55
56
await app . register ( cookieParser ) ;
56
57
58
+ // Only on routes we've decorated.
59
+ await app . register ( rawBody , { global : false } ) ;
60
+
57
61
app . setGlobalPrefix ( config . hostUrl$ . value . pathname . slice ( 1 ) ) ;
58
62
59
63
config . applyTimeouts ( app . getHttpServer ( ) , config . httpTimeouts ) ;
@@ -71,6 +75,7 @@ export class HttpAdapter extends PatchedFastifyAdapter {
71
75
72
76
const config = RouteConfig . get ( handler ) ?? { } ;
73
77
const constraints = RouteConstraints . get ( handler ) ?? { } ;
78
+ const rawBody = RawBody . get ( handler ) ;
74
79
75
80
let version : VersionValue | undefined = ( handler as any ) . version ;
76
81
version = version === VERSION_NEUTRAL ? undefined : version ;
@@ -79,13 +84,36 @@ export class HttpAdapter extends PatchedFastifyAdapter {
79
84
constraints . version = version ;
80
85
}
81
86
87
+ // Plugin configured to just add the rawBody property while continuing
88
+ // to parse the content type normally.
89
+ // Useful for signed webhook payload validation.
90
+ if ( rawBody && ! rawBody . passthrough ) {
91
+ config . rawBody = true ;
92
+ }
93
+
82
94
const route : RouteOptions = {
83
95
method,
84
96
url,
85
97
handler,
86
98
...( Object . keys ( constraints ) . length > 0 ? { constraints } : { } ) ,
87
99
...( Object . keys ( config ) . length > 0 ? { config } : { } ) ,
88
100
} ;
101
+
102
+ if ( rawBody ?. passthrough ) {
103
+ const { allowContentTypes } = rawBody ;
104
+ const contentTypes = Array . isArray ( allowContentTypes )
105
+ ? allowContentTypes . slice ( )
106
+ : ( ( allowContentTypes ?? '*' ) as string | RegExp ) ;
107
+ return this . instance . register ( async ( child ) => {
108
+ child . removeAllContentTypeParsers ( ) ;
109
+ child . addContentTypeParser (
110
+ contentTypes ,
111
+ { parseAs : 'buffer' } ,
112
+ ( req , payload , done ) => done ( null , payload ) ,
113
+ ) ;
114
+ child . route ( route ) ;
115
+ } ) ;
116
+ }
89
117
return this . instance . route ( route ) ;
90
118
}
91
119
0 commit comments