Currently, I am in the process of creating a prototype where my objective is to develop a webservice using the express framework in TypeScript, compile it to TS, and host it within a firebase functions environment. The code structure includes a Controller layer and a business logic layer, similar to what you would find in a .Net webservice. By utilizing dependency injection with the inversifyJS framework, I am able to inject the logic class into the controller.
Below is a snippet of the TypeScript code that has been written 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);
}
And this is a portion of the compiled output for 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 encounter an error where the property service1Logic is unable to be read from undefined. It appears that this is undefined and I have been unable to pinpoint the cause of this issue.
I have attempted to reproduce the error with the following 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);
}
The controller compiled code appears nearly identical:
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);
}
};
Running this code outside of the emulator, using node, does not yield any errors. It seems that 'this' in PrintText does indeed exist. I even copied the tsconfig.json content from the original project to the error-isolating project, but the issue persists. I am currently at a standstill and am unable to determine the root cause of the error. Has anyone encountered a similar issue or knows how to resolve it?
Edit: Added the calling code
In the prototype code, the methods are linked to a route.
app.get('/fastget', this.fastGet);
The '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<TYPES.IService1Controller>(TYPES.IService1Controller);
export const service1 = functionsEUWest1.https.onRequest(service1Controller.App);
Below 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 error-isolating code, the calling code is as follows:
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 'Container' objects are defined:
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;