diff --git a/packages/express-wrapper/src/index.ts b/packages/express-wrapper/src/index.ts index 7d00fe5f..f3107083 100644 --- a/packages/express-wrapper/src/index.ts +++ b/packages/express-wrapper/src/index.ts @@ -21,46 +21,59 @@ export type { ResponseEncoder } from './response'; const isHttpVerb = (verb: string): verb is 'get' | 'put' | 'post' | 'delete' => verb === 'get' || verb === 'put' || verb === 'post' || verb === 'delete'; -export const createServerWithResponseEncoder = - (encoder: ResponseEncoder) => - ( - spec: ApiSpec, - configureExpressApplication: (app: express.Application) => { - [ApiName in keyof Spec]: { - [Method in keyof Spec[ApiName]]: RouteHandler; - }; - }, - ) => { - const app: express.Application = express(); - const routes = configureExpressApplication(app); - - const router = express.Router(); - for (const apiName of Object.keys(spec)) { - const resource = spec[apiName] as Spec[string]; - for (const method of Object.keys(resource)) { - if (!isHttpVerb(method)) { - continue; - } - const httpRoute: HttpRoute = resource[method]!; - const routeHandler = routes[apiName]![method]!; - const expressRouteHandler = decodeRequestAndEncodeResponse( - apiName, - httpRoute, - // FIXME: TS is complaining that `routeHandler` is not necessarily guaranteed to be a - // `ServiceFunction`, because subtypes of Spec[string][string] can have arbitrary extra keys. - getServiceFunction(routeHandler as any), - encoder, - ); - const handlers = [...getMiddleware(routeHandler), expressRouteHandler]; +type CreateRouterProps = { + spec: Spec; + routeHandlers: { + [ApiName in keyof Spec]: { + [Method in keyof Spec[ApiName]]: RouteHandler; + }; + }; + encoder?: ResponseEncoder; +}; - const expressPath = apiTsPathToExpress(httpRoute.path); - router[method](expressPath, handlers); +export function routerForApiSpec({ + spec, + routeHandlers, + encoder = defaultResponseEncoder, +}: CreateRouterProps) { + const router = express.Router(); + for (const apiName of Object.keys(spec)) { + const resource = spec[apiName] as Spec[string]; + for (const method of Object.keys(resource)) { + if (!isHttpVerb(method)) { + continue; } - } + const httpRoute: HttpRoute = resource[method]!; + const routeHandler = routeHandlers[apiName]![method]!; + const expressRouteHandler = decodeRequestAndEncodeResponse( + apiName, + httpRoute, + // FIXME: TS is complaining that `routeHandler` is not necessarily guaranteed to be a + // `ServiceFunction`, because subtypes of Spec[string][string] can have arbitrary extra keys. + getServiceFunction(routeHandler as any), + encoder, + ); + const handlers = [...getMiddleware(routeHandler), expressRouteHandler]; - app.use(router); + const expressPath = apiTsPathToExpress(httpRoute.path); + router[method](expressPath, handlers); + } + } - return app; - }; + return router; +} -export const createServer = createServerWithResponseEncoder(defaultResponseEncoder); +export const createServer = ( + spec: Spec, + configureExpressApplication: (app: express.Application) => { + [ApiName in keyof Spec]: { + [Method in keyof Spec[ApiName]]: RouteHandler; + }; + }, +) => { + const app = express(); + const routeHandlers = configureExpressApplication(app); + const router = routerForApiSpec({ spec, routeHandlers }); + app.use(router); + return app; +};