node.jstypescriptexpressrouter

Typescript Node Express adding routes dynamically - can somebody explain this code please?


I am running an API server with node/typescript and express / express validator, and somewhere found this code which I really liked, to separate my route logic from creating routers:


function createRouter(route: Array<RouteEntry>): Router {
  const router = Router();
  route.forEach((subRoute: RouteEntry) => {
    (router as any)[subRoute.method](
      subRoute.route,
      ...subRoute.validation,
      async (req: Request, res: Response, next: NextFunction) => {
        let data = validationResult(req);
        let response = await subRoute.action(data, req, res);
      }
    );
  });
  return router;
}

So you pass this function an array of RouteEntry's:

export type RouteEntry = {
  method: string;
  route: string;
  action: (data: Record<string, any>, req?: Request, res?: Response) => any;
  validation: Array<Middleware & ContextRunner>;
};

What I don't understand (excuse my limited depth of Typescript) is this bit:

route.forEach((subRoute: RouteEntry) => {
    (router as any)[subRoute.method](...);

Clearly it works and adds routes to the Router stack, but I don't understand how typescript knows to do this? With my simple understanding, I would expect some sort of assignment such as Router[subRoute.method] = ...

any hints would be appreciated...


Solution

  • There's is apparently an array of route definitions that look like this:

    const route = [
        {method: "get", route: "/viewitem", validation: [], action: viewItem},
        {method: "get", route: "/list"}, validation: [], action: list},
        {method: "post", route: "/newitem", validation: [checkData], action: createNewItem}
    ];
    

    The code in your question iterates through this array and registers an Express route handler for each object in the array, getting the method, the path, any validation handlers and the routeHandler function from each object in the array.

    It's a parameterized way of defining routes.

    In this loop:

    route.forEach((subRoute: RouteEntry) => {
       (router as any)[subRoute.method](...);
    

    This part, gets the method value from each object and uses it as a method on the router. So, if the method in a given object was "get", then this:

    router[subRoute.method]()
    

    would be the same as:

    router["get"]()
    

    which is the same as:

    router.get()
    

    But, using the brackets allows you to use a variable as the method name which in this case comes from subRoute.method.