@@ -49,7 +49,7 @@ export class RequestValidationError extends BadRequestException {
4949 public pathParams : z . ZodError | null ,
5050 public headers : z . ZodError | null ,
5151 public query : z . ZodError | null ,
52- public body : z . ZodError | null
52+ public body : z . ZodError | null ,
5353 ) {
5454 super ( {
5555 paramsResult : pathParams ,
@@ -61,16 +61,19 @@ export class RequestValidationError extends BadRequestException {
6161}
6262
6363export class ResponseValidationError extends InternalServerErrorException {
64- constructor ( public appRoute : AppRoute , public error : z . ZodError ) {
64+ constructor (
65+ public appRoute : AppRoute ,
66+ public error : z . ZodError ,
67+ ) {
6568 super (
66- `[ts-rest] Response validation failed for ${ appRoute . method } ${ appRoute . path } : ${ error . message } `
69+ `[ts-rest] Response validation failed for ${ appRoute . method } ${ appRoute . path } : ${ error . message } ` ,
6770 ) ;
6871 }
6972}
7073
7174export const TsRestHandler = (
7275 appRouterOrRoute : AppRouter | AppRoute ,
73- options : TsRestOptions = { }
76+ options : TsRestOptions = { } ,
7477) : MethodDecorator => {
7578 const decorators = [ ] ;
7679
@@ -80,20 +83,20 @@ export const TsRestHandler = (
8083
8184 if ( options . validateResponses !== undefined ) {
8285 decorators . push (
83- SetMetadata ( ValidateResponsesSymbol , options . validateResponses )
86+ SetMetadata ( ValidateResponsesSymbol , options . validateResponses ) ,
8487 ) ;
8588 }
8689
8790 decorators . push (
8891 SetMetadata (
8992 ValidateRequestHeadersSymbol ,
90- options . validateRequestHeaders ?? true
93+ options . validateRequestHeaders ?? true ,
9194 ) ,
9295 SetMetadata (
9396 ValidateRequestQuerySymbol ,
94- options . validateRequestQuery ?? true
97+ options . validateRequestQuery ?? true ,
9598 ) ,
96- SetMetadata ( ValidateRequestBodySymbol , options . validateRequestBody ?? true )
99+ SetMetadata ( ValidateRequestBodySymbol , options . validateRequestBody ?? true ) ,
97100 ) ;
98101
99102 const isMultiHandler = ! isAppRoute ( appRouterOrRoute ) ;
@@ -112,7 +115,7 @@ export const TsRestHandler = (
112115 decorators . push (
113116 All ( routerPathsArray ) ,
114117 SetMetadata ( TsRestAppRouteMetadataKey , appRouterOrRoute ) ,
115- UseInterceptors ( TsRestHandlerInterceptor )
118+ UseInterceptors ( TsRestHandlerInterceptor ) ,
116119 ) ;
117120 } else {
118121 const apiDecorator = ( ( ) => {
@@ -133,7 +136,7 @@ export const TsRestHandler = (
133136 decorators . push (
134137 apiDecorator ,
135138 SetMetadata ( TsRestAppRouteMetadataKey , appRouterOrRoute ) ,
136- UseInterceptors ( TsRestHandlerInterceptor )
139+ UseInterceptors ( TsRestHandlerInterceptor ) ,
137140 ) ;
138141 }
139142
@@ -146,7 +149,7 @@ type NestHandlerImplementation<T extends AppRouter | AppRoute> =
146149 : {
147150 [ K in keyof T ] : T [ K ] extends AppRoute
148151 ? (
149- args : TsRestRequestShape < T [ K ] >
152+ args : TsRestRequestShape < T [ K ] > ,
150153 ) => Promise < ServerInferResponses < T [ K ] > >
151154 : never ;
152155 } ;
@@ -159,7 +162,7 @@ type NestHandlerImplementation<T extends AppRouter | AppRoute> =
159162 */
160163export const tsRestHandler = < T extends AppRouter | AppRoute > (
161164 contract : T ,
162- implementation : NestHandlerImplementation < T >
165+ implementation : NestHandlerImplementation < T > ,
163166) => implementation ;
164167
165168/**
@@ -179,7 +182,7 @@ export const doesUrlMatchContractPath = (
179182 /**
180183 * @example '/posts/1'
181184 */
182- url : string
185+ url : string ,
183186) : boolean => {
184187 // strip trailing slash
185188 if ( contractPath !== '/' && contractPath . endsWith ( '/' ) ) {
@@ -223,12 +226,12 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
223226
224227 const appRoute = this . reflector . get < AppRoute | AppRouter | undefined > (
225228 TsRestAppRouteMetadataKey ,
226- ctx . getHandler ( )
229+ ctx . getHandler ( ) ,
227230 ) ;
228231
229232 if ( ! appRoute ) {
230233 throw new Error (
231- 'Could not find app router or app route, ensure you are using the @TsRestHandler decorator on your method'
234+ 'Could not find app router or app route, ensure you are using the @TsRestHandler decorator on your method' ,
232235 ) ;
233236 }
234237
@@ -246,7 +249,7 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
246249 return (
247250 doesUrlMatchContractPath (
248251 value . path ,
249- 'path' in req ? req . path : req . routeOptions . url
252+ 'path' in req ? req . path : req . routeOptions . url ,
250253 ) && req . method === value . method
251254 ) ;
252255 }
@@ -280,13 +283,13 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
280283 | typeof ValidateResponsesSymbol
281284 | typeof ValidateRequestHeadersSymbol
282285 | typeof ValidateRequestQuerySymbol
283- | typeof ValidateRequestBodySymbol
286+ | typeof ValidateRequestBodySymbol ,
284287 ) =>
285288 Boolean (
286289 this . reflector . getAllAndOverride < boolean | undefined > ( key , [
287290 ctx . getHandler ( ) ,
288291 ctx . getClass ( ) ,
289- ] )
292+ ] ) ,
290293 ) ;
291294
292295 const paramsResult = checkZodSchema ( req . params , appRoute . pathParams , {
@@ -305,7 +308,7 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
305308
306309 const bodyResult = checkZodSchema (
307310 req . body ,
308- 'body' in appRoute ? appRoute . body : null
311+ 'body' in appRoute ? appRoute . body : null ,
309312 ) ;
310313
311314 const isValidationEnabled = getMetadataValue ( ValidateResponsesSymbol ) ;
@@ -329,7 +332,7 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
329332 ! paramsResult . success ? paramsResult . error : null ,
330333 isHeadersInvalid ? headersResult . error : null ,
331334 isQueryInvalid ? queryResult . error : null ,
332- isBodyInvalid ? bodyResult . error : null
335+ isBodyInvalid ? bodyResult . error : null ,
333336 ) ;
334337 }
335338
@@ -350,7 +353,7 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
350353 result = {
351354 status : e . getStatus ( ) ,
352355 body : e . getResponse ( ) ,
353- error : true ,
356+ error : e ,
354357 } ;
355358 } else {
356359 throw e ;
@@ -362,7 +365,16 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
362365 : result ;
363366
364367 const responseType = appRoute . responses [ result . status ] ;
365- if ( ! result . error && isAppRouteOtherResponse ( responseType ) ) {
368+
369+ if ( result . error ) {
370+ throw new HttpException (
371+ responseAfterValidation . body ,
372+ responseAfterValidation . status ,
373+ {
374+ cause : result . error ,
375+ } ,
376+ ) ;
377+ } else if ( ! result . error && isAppRouteOtherResponse ( responseType ) ) {
366378 if ( 'setHeader' in res ) {
367379 res . setHeader ( 'content-type' , responseType . contentType ) ;
368380 } else {
@@ -372,15 +384,15 @@ export class TsRestHandlerInterceptor implements NestInterceptor {
372384
373385 res . status ( responseAfterValidation . status ) ;
374386 return responseAfterValidation . body ;
375- } )
387+ } ) ,
376388 ) ;
377389 }
378390}
379391
380392const validateResponse = (
381393 appRoute : AppRoute ,
382- response : { status : number ; body ?: unknown }
383- ) => {
394+ response : { status : number ; body ?: unknown } ,
395+ ) : { body : unknown ; status : number } => {
384396 const { body } = response ;
385397
386398 const responseType = appRoute . responses [ response . status ] ;
@@ -390,13 +402,13 @@ const validateResponse = (
390402
391403 if ( ! responseSchema ) {
392404 throw new InternalServerErrorException (
393- `[ts-rest] Couldn't find a response schema for ${ response . status } on route ${ appRoute . path } `
405+ `[ts-rest] Couldn't find a response schema for ${ response . status } on route ${ appRoute . path } ` ,
394406 ) ;
395407 }
396408
397409 const responseValidation = checkZodSchema (
398410 body ,
399- appRoute . responses [ response . status ]
411+ appRoute . responses [ response . status ] ,
400412 ) ;
401413
402414 if ( ! responseValidation . success ) {
0 commit comments