https://serverless-stack.com/ logo
#help
Title
# help
u

Uncharted

03/18/2022, 1:41 PM
Hello everyone ! I was wondering if someone tried to use injection like inversifyjs with SST ? I tried to implement an API with a route targeting the code below but got an error like it doesn't find/resolve but it works if I instantiate the class without injection
Copy code
export interface IService {
  handle(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult>;
}

@injectable()
export class TestInject implements IService {
  public async handle(event: APIGatewayProxyEvent) {
    console.log('test inject');
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ success: true }),
    };
  }
}

const service = myContainer.get<IService>(TYPES.IService);
export const handle = service.handle;
Result:
Copy code
Unhandled Promise Rejection     {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"TypeError: Cannot read property 'name' of undefined","reason":"TypeError: Cannot read property 'name' of undefined","promise":{},"stack":["Runtime.UnhandledPromiseRejection: TypeError: Cannot read property 'name' of undefined","    at process.<anonymous> (file:///home/uncharted/projects/my-api/node_modules/@serverless-stack/aws-lambda-ric/lib/index.js:34:23)","    at process.emit (events.js:376:20)","    at processPromiseRejections (internal/process/promises.js:245:33)","    at processTicksAndRejections (internal/process/task_queues.js:96:32)"]}
a

Adam Fanello

03/18/2022, 3:17 PM
There's hoops you have to jump through to get Typescript decorators to properly work through SST. SST uses esbuild, which is much faster than tsc but part of the speed is that eslint simply strips Typescript down to Javascript without doing much with it - like not generating code from decorators.
There's some bit in the SST documentation with an example. The sailplane/injector library (similar to inversify) has some instructions as well: https://docs.onica.com/projects/sailplane/en/latest/injector.html#configuration
u

Uncharted

03/18/2022, 3:19 PM
Ok i will try that 😀
Between the two docs, you should have everything you need.
@Frank I suggest added documentation in SST to explicitly call out Typescript decorators. The bit you have right now is actually documenting how to use esbuild plugins, and coincidentally is using the decorators plugin as an example. That example is incomplete, missing the required tsconfig.json changes.
a

Artemiy Davydov

03/18/2022, 7:25 PM
🥄 How to get decorators to work: stack.js
Copy code
...  
  const api = new sst.Api(this, "Api", {
      defaultFunctionProps: {
        bundle: {
          esbuildConfig: {
            plugins: "esbuild-decorators-plugin.js",
          },
        },
...
esbuild-decorators-plugin.js
Copy code
/* eslint-disable @typescript-eslint/no-var-requires */

const { esbuildDecorators } = require("@anatine/esbuild-decorators");

module.exports = [esbuildDecorators()];
tsconfig.json
Copy code
...  
"compilerOptions": {
...
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
a

Adam Fanello

03/18/2022, 7:29 PM
Need a couple config options in tsconfig.json as well. See the sailplane link above ☝️
a

Artemiy Davydov

03/19/2022, 7:40 AM
@Adam Fanello Updated snippets. I really forgot about tsconfig
u

Uncharted

03/19/2022, 9:46 AM
hi That's what I did but I still got the same pb, I copy pasted also your exemple. I wonder if it comes from inversify
Copy code
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { injectable } from 'inversify';
import 'reflect-metadata';
import { myContainer } from './inversify.config';
import { TYPES } from './types';

export interface IService {
  handle(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult>;
}
@injectable()
export class TestInject implements IService {
  public async handle(
    event: APIGatewayProxyEvent
  ): Promise<APIGatewayProxyResult> {
    console.log('test inject');
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ success: true }),
    };
  }
}

const service = myContainer.get<IService>(TYPES.IService);
// const service = new TestInject()
export const handle = service.handle;
types.ts
Copy code
const TYPES = {
  IService: Symbol.for('IService'),
};

export { TYPES };
inversify.config.ts
Copy code
import { Container } from 'inversify';
import { TYPES } from './types';
import { IService, TestInject } from './testinject';

const myContainer = new Container();
myContainer.bind<IService>(TYPES.IService).to(TestInject);

export { myContainer };
stack
Copy code
new sst.Api(this, 'Api', {
      routes: {
        'POST /testinject': {
          function: {
            bundle: {
              esbuildConfig: {
                plugins: 'src/esbuild-decorators-plugin.js',
              },
            },
            srcPath: 'src/',
            handler: 'testinject.handle',
          },
        },
      },
    });
tsconfig.json
Copy code
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "commonjs",
    "lib": ["es2018"],
    "declaration": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": false,
    "inlineSourceMap": true,
    "inlineSources": true,
    "strictPropertyInitialization": false,
    "typeRoots": ["./node_modules/@types"],
    "esModuleInterop": true,
    "types": ["reflect-metadata"],
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
}
I m sure it's something silly I miss but I don't see what need my plastic duck
ok it works with @sailplane/injector I must miss something with inversify
it works with tsyringe too and dont need to use the esbuild decorator plugin
f

Frank

03/20/2022, 2:26 AM
@Adam Fanello yup will add ts decorators to doc https://github.com/serverless-stack/serverless-stack/issues/1554
@Uncharted did u get this to work? Would love to add this to the doc as well.
u

Uncharted

03/21/2022, 11:01 AM
@Frank Well I kept tsyringe it seems to work without doing anythin (no esbuild plugin) except adding the options in the tsconfig.json but its needed for every injection framework anyway
didnt check for sailplane framework but i think it works too without the esbuild plugin I had only the problem with inversify quite strange because most framework in the same way 🤔 I missed probably something with inversify but don't know that yet
But to tell you why I wanted to use an injection framework is that I wanted to implement DDD/CQRS with sst and for example nestJS propose some features to make it more easy
a

Adam Fanello

03/21/2022, 2:38 PM
The esbuild plugin is required to generate the code behind the decorators. Everything will compile and deploy, but fail at runtime. It isn't a library thing - esbuild will strip out decorators without generating code unless you have a plugin that forces it to compile with tsc, which will generate code for decorators.
u

Uncharted

03/21/2022, 2:40 PM
well i used the default configuration of sst and i tried to not put the esbuild plugin / bundle part in the function declaration and it worked 🤔
2 Views