redis / redis-om-node

Object mapping, and more, for Redis and Node.js. Written in TypeScript.
MIT License
1.18k stars 80 forks source link

Tsyringe - losing reference to "this" when using "this.xxrepository" #172

Closed argupta23 closed 1 year ago

argupta23 commented 1 year ago

I am using Tsyringe in my controller along with Express4

My controller code looks something like this

import { Request, Response, NextFunction } from "express";
import { autoInjectable, container } from "tsyringe";
import { Repository} from 'redis-om';
import { redisClient } from "../utils";

@autoInjectable()
export class DeviceController {

  private deviceRepository = new Repository(DeviceSchemaR, redisClient);

public async create(req: Request, res: Response, next: NextFunction) {

    let tplate = await DeviceModel.create([{ ...newEntry }], { session: session });  // this is call to mongoose/mongodb
Case 1:
    let tplateR = await this.deviceRepository.save( tplate.id, tplate); // resp is saved to redis

case 2:
   var self = this;
    let tplateR = await self.deviceRepository.save( tplate.id, tplate); // resp is saved to redis
...

When we hit the last line it results in the below error and is true for both the cases

TypeError: Cannot read properties of undefined (reading 'deviceRepository')
    at /home/server/src/controllers/device.controller.ts:78:40
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async create (/home/server/src/controllers/device.controller.ts:70:31)

breakpoint within the code indicates that "this" is undefined.

Additionally my tsconfig.json contains

{
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs"
    }
  },
  "compilerOptions": {
    "moduleResolution": "node",
    "module":  "esnext", //  "commonjs",
    "target": "esnext",
    "rootDir": "./src",
    "outDir": "./build",
    "esModuleInterop": true,
    "strict": true,
    "noImplicitAny": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "strictPropertyInitialization": false,
    "allowSyntheticDefaultImports": true,
    "useUnknownInCatchVariables": false,
    "resolveJsonModule": true,
    "sourceMap": true,
    "allowJs": true,
    "skipLibCheck": true,
    "typeRoots": [
      "./src/types",
      "./node_modules/@types"
    ]
  },
  "include": [
    "src/**/*"
, "src/routes/.route.ts"  ],
  "typeRoots": [
    "src/@types", 
    "./node_modules/@types"
    ],                  /* List of folders to include type definitions from. */

}

Any guidance on how to solve this is greatly appreciated

Thanks

argupta23 commented 1 year ago

I think a possible way to get around the issue is to do the following


  constructor(
    @inject("DeviceRepository")
    private deviceRepository: Repository????
  ) {}

or

 private deviceRepository = container.resolve(Repository ????);

what would the syntax for Repository be?

Thanks

guyroyse commented 1 year ago

Repository in Redis OM is just a class. It's not doing anything that terribly interesting, at least not interesting in the sense that it would affect the scope of this. I don't believe this error has anything to do with Redis OM, primarily because the error you are receiving is complaining that the value of this is undefined. Redis OM hasn't had a chance to do anything yet.

My guess—and I've never heard of tsyringe beyond a quick google 2 minutes ago—is that you need to inject an instantiated Repository into your Controller. But I have no expertise here. I don't know how it works. I've just used DI frameworks in the last 20 years. ;)

Regardless, do you think this is an error with Redis OM or are you just looking for some help with a third-party library? If the former, I think I need more proof. If the latter, it might be better asked elsewhere.

I will say that the library looks interesting and I may well check it out when I get some time.

alan-pg commented 1 year ago

I think there are issues with tsyring in typescript 5 because of some decorators changes in version 5, check issues in tsyring.

argupta23 commented 1 year ago

thanks for the responses. @alan-pg

I am still using typescript 4 in my case.

@guyroyse

I am in the process of removing "tsyringe", but have a question for you.

In the current implementation I have the schema defined in a *.model.ts and then within my controller I use the following lines

save
            let redisW = await new Repository(DeviceSchemaR, redisClient).save(tplateJson.id, tplateJson);

get
      let redisO = new Repository(DeviceSchemaR, redisClient);
      await redisO.createIndex();  <-- is this the right place for this call? 
      let redisOResp = await redisO.search().where('tntid').equals(tntid).and('userId').equals(uid).return.all();

While this works, can you please let me know if this is the right way or should I be doing this differently?

More specifically the "createIndex()"?

Appreciate the guidance.

guyroyse commented 1 year ago

What I typically do it define a Schema and Repository in a separate module. In that module I export the Repository and call .createIndex().

Like this:

import { redisClient } from './client'.

const deviceSchema = new Schema(...stuff goes here...)
export const deviceRepository = new Repository(deviceSchema, redisClient)

await deviceRepository.createIndex()

I also use a separate module to create and export the Node Redis client which you see in the first line of my code.

Then, I can just import the redisClient or the deviceRepository as needed.