Skip to content

Commit

Permalink
Merge pull request #79 from bitgopatmcl/express-router-fn
Browse files Browse the repository at this point in the history
Add router-returning function to express-wrapper
  • Loading branch information
ericcrosson-bitgo authored May 16, 2022
2 parents 10c2ab1 + 03468c2 commit f41013f
Showing 1 changed file with 51 additions and 38 deletions.
89 changes: 51 additions & 38 deletions packages/express-wrapper/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 extends ApiSpec>(
spec: ApiSpec,
configureExpressApplication: (app: express.Application) => {
[ApiName in keyof Spec]: {
[Method in keyof Spec[ApiName]]: RouteHandler<Spec[ApiName][Method]>;
};
},
) => {
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 extends ApiSpec> = {
spec: Spec;
routeHandlers: {
[ApiName in keyof Spec]: {
[Method in keyof Spec[ApiName]]: RouteHandler<Spec[ApiName][Method]>;
};
};
encoder?: ResponseEncoder;
};

const expressPath = apiTsPathToExpress(httpRoute.path);
router[method](expressPath, handlers);
export function routerForApiSpec<Spec extends ApiSpec>({
spec,
routeHandlers,
encoder = defaultResponseEncoder,
}: CreateRouterProps<Spec>) {
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 extends ApiSpec>(
spec: Spec,
configureExpressApplication: (app: express.Application) => {
[ApiName in keyof Spec]: {
[Method in keyof Spec[ApiName]]: RouteHandler<Spec[ApiName][Method]>;
};
},
) => {
const app = express();
const routeHandlers = configureExpressApplication(app);
const router = routerForApiSpec({ spec, routeHandlers });
app.use(router);
return app;
};

0 comments on commit f41013f

Please sign in to comment.