@@ -5,9 +5,12 @@ import { pipe } from 'fp-ts/function';
55import { defaultOnDecodeError , defaultOnEncodeError } from './errors' ;
66import { apiTsPathToExpress } from './path' ;
77import {
8- AddAliasRouteHandler ,
98 AddRouteHandler ,
9+ AddUncheckedRouteHandler ,
1010 Methods ,
11+ UncheckedRequestHandler ,
12+ WrappedRequest ,
13+ WrappedResponse ,
1114 WrappedRouteOptions ,
1215 WrappedRouter ,
1316 WrappedRouterOptions ,
@@ -18,6 +21,7 @@ export type {
1821 OnDecodeErrorFn ,
1922 OnEncodeErrorFn ,
2023 TypedRequestHandler ,
24+ UncheckedRequestHandler ,
2125 WrappedRouter ,
2226 WrappedRouteOptions ,
2327 WrappedRouterOptions ,
@@ -64,72 +68,94 @@ export function wrapRouter<Spec extends ApiSpec>(
6468 onDecodeError = defaultOnDecodeError ,
6569 onEncodeError = defaultOnEncodeError ,
6670 afterEncodedResponseSent = ( ) => { } ,
67- } : WrappedRouteOptions < HttpRoute > ,
71+ } : WrappedRouteOptions ,
6872) : WrappedRouter < Spec > {
69- function makeAddAliasRoute < Method extends Methods > (
73+ const routerMiddleware : UncheckedRequestHandler [ ] = [ ] ;
74+
75+ function makeAddUncheckedRoute < Method extends Methods > (
7076 method : Method ,
71- ) : AddAliasRouteHandler < Spec , Method > {
72- return ( path , apiName , handlers , options ) => {
77+ ) : AddUncheckedRouteHandler < Spec , Method > {
78+ return ( apiName , handlers , options ) => {
7379 const route : HttpRoute = spec [ apiName as keyof Spec ] ! [ method ] ! ;
74- const wrapReqAndRes : express . RequestHandler = ( req , res , next ) => {
75- pipe (
76- route . request . decode ( req ) ,
77- E . matchW (
78- ( errs ) => ( options ?. onDecodeError ?? onDecodeError ) ( errs , req , res ) ,
79- ( decoded ) => {
80- // Gotta cast to mutate this in place
81- ( req as any ) . decoded = decoded ;
82- ( res as any ) . sendEncoded = (
83- status : keyof typeof route [ 'response' ] ,
84- payload : any ,
85- ) => {
86- try {
87- const codec = route . response [ status ] ;
88- if ( ! codec ) {
89- throw new Error ( `no codec defined for response status ${ status } ` ) ;
90- }
91- const statusCode =
92- typeof status === 'number'
93- ? status
94- : KeyToHttpStatus [ status as keyof KeyToHttpStatus ] ;
95- if ( statusCode === undefined ) {
96- throw new Error ( `unknown HTTP status code for key ${ status } ` ) ;
97- } else if ( ! codec . is ( payload ) ) {
98- throw new Error (
99- `response does not match expected type ${ codec . name } ` ,
100- ) ;
101- }
102- const encoded = codec . encode ( payload ) ;
103- res . status ( statusCode ) . json ( encoded ) . end ( ) ;
104- ( options ?. afterEncodedResponseSent ?? afterEncodedResponseSent ) (
105- status ,
106- payload ,
107- req ,
108- res ,
109- ) ;
110- } catch ( err ) {
111- ( options ?. onEncodeError ?? onEncodeError ) ( err , req , res ) ;
112- }
113- } ;
114- next ( ) ;
115- } ,
116- ) ,
117- ) ;
80+ const wrapReqAndRes : UncheckedRequestHandler = ( req , res , next ) => {
81+ const decoded = route . request . decode ( req ) ;
82+ req . decoded = decoded ;
83+ req . apiName = apiName ;
84+ req . httpRoute = route ;
85+ res . sendEncoded = (
86+ status : keyof typeof route [ 'response' ] ,
87+ payload : unknown ,
88+ ) => {
89+ try {
90+ const codec = route . response [ status ] ;
91+ if ( ! codec ) {
92+ throw new Error ( `no codec defined for response status ${ status } ` ) ;
93+ }
94+ const statusCode =
95+ typeof status === 'number'
96+ ? status
97+ : KeyToHttpStatus [ status as keyof KeyToHttpStatus ] ;
98+ if ( statusCode === undefined ) {
99+ throw new Error ( `unknown HTTP status code for key ${ status } ` ) ;
100+ } else if ( ! codec . is ( payload ) ) {
101+ throw new Error ( `response does not match expected type ${ codec . name } ` ) ;
102+ }
103+ const encoded = codec . encode ( payload ) ;
104+ res . status ( statusCode ) . json ( encoded ) . end ( ) ;
105+ ( options ?. afterEncodedResponseSent ?? afterEncodedResponseSent ) (
106+ statusCode ,
107+ payload ,
108+ req as WrappedRequest ,
109+ res as WrappedResponse ,
110+ ) ;
111+ } catch ( err ) {
112+ ( options ?. onEncodeError ?? onEncodeError ) (
113+ err ,
114+ req as WrappedRequest ,
115+ res as WrappedResponse ,
116+ ) ;
117+ }
118+ } ;
119+ next ( ) ;
118120 } ;
119121
120- router [ method ] ( path , [ wrapReqAndRes , ...( handlers as express . RequestHandler [ ] ) ] ) ;
122+ const middlewareChain = [
123+ wrapReqAndRes ,
124+ ...routerMiddleware ,
125+ ...handlers ,
126+ ] as express . RequestHandler [ ] ;
127+
128+ const path = spec [ apiName as keyof typeof spec ] ! [ method ] ! . path ;
129+ router [ method ] ( apiTsPathToExpress ( path ) , middlewareChain ) ;
130+
131+ options ?. routeAliases ?. forEach ( ( alias ) => {
132+ router [ method ] ( alias , middlewareChain ) ;
133+ } ) ;
121134 } ;
122135 }
123136
124137 function makeAddRoute < Method extends Methods > (
125138 method : Method ,
126139 ) : AddRouteHandler < Spec , Method > {
127140 return ( apiName , handlers , options ) => {
128- const path = spec [ apiName as keyof typeof spec ] ! [ method ] ! . path ;
129- return makeAddAliasRoute ( method ) (
130- apiTsPathToExpress ( path ) ,
141+ const validateMiddleware : UncheckedRequestHandler = ( req , res , next ) => {
142+ pipe (
143+ req . decoded ,
144+ E . matchW (
145+ ( errs ) => {
146+ ( options ?. onDecodeError ?? onDecodeError ) ( errs , req , res ) ;
147+ } ,
148+ ( value ) => {
149+ req . decoded = value ;
150+ next ( ) ;
151+ } ,
152+ ) ,
153+ ) ;
154+ } ;
155+
156+ return makeAddUncheckedRoute ( method ) (
131157 apiName ,
132- handlers ,
158+ [ validateMiddleware , ... handlers ] ,
133159 options ,
134160 ) ;
135161 } ;
@@ -144,14 +170,13 @@ export function wrapRouter<Spec extends ApiSpec>(
144170 post : makeAddRoute ( 'post' ) ,
145171 put : makeAddRoute ( 'put' ) ,
146172 delete : makeAddRoute ( 'delete' ) ,
147- getAlias : makeAddAliasRoute ( 'get' ) ,
148- postAlias : makeAddAliasRoute ( 'post' ) ,
149- putAlias : makeAddAliasRoute ( 'put' ) ,
150- deleteAlias : makeAddAliasRoute ( 'delete' ) ,
151- getUnchecked : router . get ,
152- postUnchecked : router . post ,
153- putUnchecked : router . put ,
154- deleteUnchecked : router . delete ,
173+ getUnchecked : makeAddUncheckedRoute ( 'get' ) ,
174+ postUnchecked : makeAddUncheckedRoute ( 'post' ) ,
175+ putUnchecked : makeAddUncheckedRoute ( 'put' ) ,
176+ deleteUnchecked : makeAddUncheckedRoute ( 'delete' ) ,
177+ use : ( middleware : UncheckedRequestHandler ) => {
178+ routerMiddleware . push ( middleware ) ;
179+ } ,
155180 } ,
156181 ) ;
157182
0 commit comments