Express JS: Cannot read property 'lazyrouter' of undefined

Viewed 2780

I'm creating a layer of abstraction around Express JS and I've encountered a problem. Considering the following code (see bottom), when calling registerRoute and hitting the line:

expressFnc(route, (req: any, res: any, next: any) => dummy(req, res, next));

I am encountering the error:

Cannot read property 'lazyrouter' of undefined

In this context 'this', I assume, applies to ExpressJS (lib/application.js:479:10).

const express = require("express");

export class ExpressHttpHost implements IHttpHost {
  private host: any = express();
  private contentResolver?: ContentResolver;

  public configure(contentResolver: ContentResolver): void {
    this.contentResolver = contentResolver;
  }

  public registerRoute(
    route: string,
    verb: HttpVerb,
    handler: (request: HttpRequest) => HttpResponse,
  ): void {
    switch (verb) {
      case HttpVerb.Get:
        this.createDummyRequest(this.host.get, route, handler);
        break;
      case HttpVerb.Delete:
        this.createDummyRequest(this.host.delete, route, handler);
        break;
      case HttpVerb.Options:
        this.createDummyRequest(this.host.options, route, handler);
        break;
      case HttpVerb.Patch:
        this.createDummyRequest(this.host.patch, route, handler);
        break;
      case HttpVerb.Post:
        this.createDummyRequest(this.host.post, route, handler);
        break;
      case HttpVerb.Put:
        this.createDummyRequest(this.host.put, route, handler);
        break;
      case HttpVerb.All:
        this.createDummyRequest(this.host.get, route, handler);
        this.createDummyRequest(this.host.delete, route, handler);
        this.createDummyRequest(this.host.options, route, handler);
        this.createDummyRequest(this.host.patch, route, handler);
        this.createDummyRequest(this.host.post, route, handler);
        this.createDummyRequest(this.host.put, route, handler);
        break;
      default:
        throw new Error("Verb is not supported.");
    }
  }

  public start(options: IHttpHostOptions): void {
    this.host.listen(options.port ? options.port : 80);
  }

  private createHttpRequest(expressReq: any): HttpRequest {
    return new HttpRequest(
      expressReq.headers,
      expressReq.params,
      expressReq.body,
    );
  }

  private createDummyRequest(expressFnc: any, route: string, handler: (request: HttpRequest) => HttpResponse): void {
    const dummy = (expressReq: any, expressRes: any, next: any) => {
      const httpRequest = this.createHttpRequest(expressReq);

      const response = handler(httpRequest);
      const responseStatus = response.status();
      const responseMimeType = response.mimeType();
      const responseContent = response.content(this.contentResolver);

      expressRes.set("Content-Type", responseMimeType);
      expressRes.status(responseStatus);
      expressRes.send(responseContent);
      next();
    };
    expressFnc(route, (req: any, res: any, next: any) => dummy(req, res, next));
  }
}
2 Answers

This might happen because express functions when passed as arguments loose reference to the host object. Try manually binding them back. See the bindings below in the switch:

const express = require("express");

export class ExpressHttpHost implements IHttpHost {
  private host: any = express();
  private contentResolver?: ContentResolver;

  public configure(contentResolver: ContentResolver): void {
    this.contentResolver = contentResolver;
  }

  public registerRoute(
    route: string,
    verb: HttpVerb,
    handler: (request: HttpRequest) => HttpResponse,
  ): void {
    switch (verb) {
      case HttpVerb.Get:
        this.createDummyRequest(this.host.get.bind(this.host), route, handler);
        break;
      case HttpVerb.Delete:
        this.createDummyRequest(this.host.delete.bind(this.host), route, handler);
        break;
      case HttpVerb.Options:
        this.createDummyRequest(this.host.options.bind(this.host), route, handler);
        break;
      case HttpVerb.Patch:
        this.createDummyRequest(this.host.patch.bind(this.host), route, handler);
        break;
      case HttpVerb.Post:
        this.createDummyRequest(this.host.post.bind(this.host), route, handler);
        break;
      case HttpVerb.Put:
        this.createDummyRequest(this.host.put.bind(this.host), route, handler);
        break;
      case HttpVerb.All:
        this.createDummyRequest(this.host.get.bind(this.host), route, handler);
        this.createDummyRequest(this.host.delete.bind(this.host), route, handler);
        this.createDummyRequest(this.host.options.bind(this.host), route, handler);
        this.createDummyRequest(this.host.patch.bind(this.host), route, handler);
        this.createDummyRequest(this.host.post.bind(this.host), route, handler);
        this.createDummyRequest(this.host.put.bind(this.host), route, handler);
        break;
      default:
        throw new Error("Verb is not supported.");
    }
  }

  public start(options: IHttpHostOptions): void {
    this.host.listen(options.port ? options.port : 80);
  }

  private createHttpRequest(expressReq: any): HttpRequest {
    return new HttpRequest(
      expressReq.headers,
      expressReq.params,
      expressReq.body,
    );
  }

  private createDummyRequest(expressFnc: any, route: string, handler: (request: HttpRequest) => HttpResponse): void {
    const dummy = (expressReq: any, expressRes: any, next: any) => {
      const httpRequest = this.createHttpRequest(expressReq);

      const response = handler(httpRequest);
      const responseStatus = response.status();
      const responseMimeType = response.mimeType();
      const responseContent = response.content(this.contentResolver);

      expressRes.set("Content-Type", responseMimeType);
      expressRes.status(responseStatus);
      expressRes.send(responseContent);
      next();
    };
    expressFnc(route, (req: any, res: any, next: any) => dummy(req, res, next));
  }
}

This problem happened to me and took me hours and extra support to resolve. My advice is to read the compilation error well, because it should tell you where in your codebase the error is caused.

In my case it was caused by

server.post(`/url/path/to/page`, myMiddleware(), MyClass.myNonExistentFunction);

The reference to a non-existent function had crept into my codebase when during a complicated merge.

Part of the problem with this compilation error is that it doesn't name the undefined callback - in this case 'MyClass.myNonExistentFunction'.

Related