0

I am working on a prototype with a goal to write a webservice with the express framework in TypeScript, compile it to TS and host it in a firebase functions environment. The code layers in the service is a Controller layer and for now, also a business logic layer for the business rules. Perhaps mostly the same layers seen in a .Net written webservice. With dependency injection using the inversifyJS framework I inject the logic class into the controller.

Here parts of the TypeScript code so far:

@injectable()
export class Service1Controller extends ApiController implements IService1Controller {
    public constructor(
        @inject(TYPES.IService1Logic)
        private service1Logic: IService1Logic
    ) { super() }

    private fastGet(request: Request, response: Response) {
        const original = request.query.text;
        const changed = this.service1Logic.UpdateFastGet(original);
        response.status(200).send(changed);    
    }
}


export interface IService1Logic {
    UpdateFastGet(text: string): string;
}

@injectable()
export class Service1Logic implements IService1Logic {

    UpdateFastGet(text: string): string {
        console.log(`[Service1Logic/UpdateFastGet]: ${text}`);
        text = `${text} -> service1[fastget]`;
        return text;
    }
}

And this is (partly) the compile output of the Controller code

constructor(service1Logic) {
    super();
    this.service1Logic = service1Logic;
}
fastGet(request, response) {
    const original = request.query.text;
    const changed = this.service1Logic.UpdateFastGet(original);
    response.status(200).send(changed);
}

When testing the code locally using the firebase functions emulator I get an error that property service1Logic cannot be read from undefined. So it seems this is undefined and I cannot figure out why.

I also tried to isolate the error and to reproduce it with the next code:

@injectable()
export class Class1 extends baseClass implements Interface1 {

    constructor(
        @inject(TYPES.Interface2)
        private class2: Interface2
     ) { super() }

    PrintText(text: string): void {
        const mutatedText: string = this.class2.MutateText(text);
        console.log(mutatedText);
    }

    Addition(x: number, y: number): number {
        return this.Sum(x, y);
    }
}

export interface Interface2 {
    MutateText(text: string): string;
}

@injectable()
export class Class2 implements Interface2 {
    MutateText(text: string): string {
        console.log(`Received text ${text}`);
        return `${text} mutated`;
    }
}

The controller compiled code looks almost the same (with exception of the naming ofcourse)

let Class1 = class Class1 extends baseclass_1.baseClass {
    constructor(class2) {
        super();
        this.class2 = class2;
    }
    PrintText(text) {
        const mutatedText = this.class2.MutateText(text);
        console.log(mutatedText);
    }
    Addition(x, y) {
        return this.Sum(x, y);
    }
};

When I run this code (not in an emulator but with node) I get no errors at all, it seems that this in PrintText does exists. I also copied the tsconfig.json content from the original project to the project where I try to isolate the error, perhaps it got to do with a setting there but that wasn't the case either as far as I can see. I am stuck on the error and I cannot figure out what is causing the error. Has anyone had this kind of issue or knows how I can solve this?

Edit: Added the calling code

For the prototype code, the methods are linked to a route.

app.get('/fastget', this.fastGet);

App is of type Express:

this.app = express();

and app is returned via a getter in the ApiController and is then added to the onRequest of firebase:

const service1Controller: IService1Controller = container.get<IService1Controller>(TYPES.IService1Controller);

export const service1 =  functionsEUWest1.https.onRequest(service1Controller.App); 

And these are the Types and container objects:

Types.ts

let TYPES = {
    //Controllers
    IService1Controller: Symbol("IService1Controller"),

    //BusinessLogics        
    IService1Logic: Symbol("IService1Logic")
}

export default TYPES;

inversify.config.ts

class ContainerConfig {
    private _container: Container = new Container;

    get Container() { return this._container };

    private configureControllers(container: Container) {
        container.bind(TYPES.IService1Controller).to(Service1Controller);
    }

    private configureBusinessLogics(container: Container) {
        container.bind(TYPES.IService1Logic).to(Service1Logic);
    }

    constructor() {
        this.configureControllers(this._container);
        this.configureBusinessLogics(this._container);
    }
}

export default new ContainerConfig().Container;

For the code to isolate the error, this is the calling code:

const class1: Interface1 = container.get<Interface1>(TYPES.Interface1);

class1.PrintText("This text is");
const result = class1.Addition(2, 3);

console.log(`Sum of 2 and 3 is ${result}`);

This is how the TYPES and the container objects and the looks like:

Types.ts

let TYPES = {
    Interface1: Symbol("Interface1"),
    Interface2: Symbol("Interface2")
}

export default TYPES;

intensify.config.ts

const container = new Container();

container.bind(TYPES.Interface1).to(Class1);
container.bind(TYPES.Interface2).to(Class2);

export default container;
Cornelis
  • 1,729
  • 5
  • 23
  • 39

1 Answers1

0

After doing some more research, it seems that when passing a handler to app.get, the this in the class where the handler is implemented is not known anymore in the handler. Searching more with this knowledge, the next topic was found:

Express JS 'this' undefined after routing with app.get(..)

it appears that a .bind can be called to the method. The solution is to bind this to fastget:

 app.get('/fastget', this.fastGet.bind(this));

and all works fine.

Cornelis
  • 1,729
  • 5
  • 23
  • 39