Search code examples
node.jstypescriptsequelize.jssequelize-typescriptinversifyjs

How to inversify sequelize instance


I have a sequelize-typescript that defines a mysql db object:

const DBInstance = new Sequelize(config.db.database, config.db.username, config.db.password, {
  host: config.db.host,
  dialect: config.db.dialect as Dialect,
  repositoryMode: true,
});

export default DBInstance;

And then I try to inject this instance to the repository class:

@injectable()
export default class AppDataRepository implements IAppMetaDataRepository {
  private readonly repository: Repository<AppData>;

  constructor(db: Sequelize) {
    this.repository = db.getRepository(AppMetaData);
  }

  findOne = async (): Promise<AppEntity | null> => {
    const result = await this.repository.findOne<AppData>();
    if (!result) {
      return null;
    }
    const data = result.get();
    return new AppDataEntity(data.id, data.name, data.version, data.createdAt, data.updatedAt);
  };
}

With the binding in config:

import MySqlInstance from '@DBConnection/MysqlInstance';

container.bind<Sequelize>(types.INVERSIFY_SYMBOL.Sequelize).toConstantValue(MySqlInstance);

Where the MySqlInstance is the instance exported DBInstance. But the above code still generates the error:

No matching bindings found for serviceIdentifier: Sequelize {"stack":"Error: No matching bindings found for serviceIdentifier: Sequelize

I have to use a Sequelize instance with the config value that I provided in the DBInstance initialization but not sure how can I do that with inversify?


Solution

  • Directory structure:

    .
    ├── db.ts
    ├── index.ts
    ├── ioc
    │   ├── interfaces.ts
    │   ├── ioc.ts
    │   └── types.ts
    ├── models
    │   ├── AppMetaData.ts
    │   └── index.ts
    └── repositories
        └── AppDataRepository.ts
    
    3 directories, 8 files
    

    models/AppMetaData.ts:

    import { Model, Table } from 'sequelize-typescript';
    
    @Table
    export class AppMetaData extends Model {}
    

    models/index.ts:

    export * from './AppMetaData';
    

    ioc/interfaces.ts:

    import { Sequelize } from 'sequelize-typescript';
    
    export type SequelizeInstance = InstanceType<typeof Sequelize>;
    
    export interface IAppMetaDataRepository {
      test(): void;
      findOne(): Promise<any>;
    }
    

    ioc/types.ts:

    const TYPES = {
      Sequelize: Symbol.for('Sequelize'),
      AppDataRepository: Symbol.for('AppDataRepository'),
    };
    
    export { TYPES };
    

    ioc/ioc.ts:

    import { Container } from 'inversify';
    import MySqlInstance from '../db';
    import { TYPES } from './types';
    import AppDataRepository from '../repositories/AppDataRepository';
    import { SequelizeInstance } from './interfaces';
    
    const container = new Container();
    container.bind<SequelizeInstance>(TYPES.Sequelize).toConstantValue(MySqlInstance);
    container.bind<AppDataRepository>(TYPES.AppDataRepository).to(AppDataRepository);
    
    export { container };
    

    repositories/AppDataRepository.ts:

    import { inject, injectable } from 'inversify';
    import { Repository } from 'sequelize-typescript';
    import { IAppMetaDataRepository, SequelizeInstance } from '../ioc/interfaces';
    import { TYPES } from '../ioc/types';
    import { AppMetaData } from '../models/AppMetaData';
    
    @injectable()
    export default class AppDataRepository implements IAppMetaDataRepository {
      private sequelize: SequelizeInstance;
      private readonly repository: Repository<AppMetaData>;
    
      constructor(@inject(TYPES.Sequelize) sequelize: SequelizeInstance) {
        this.sequelize = sequelize;
        this.repository = sequelize.getRepository(AppMetaData);
      }
    
      test() {
        console.log(typeof this.sequelize.authenticate === 'function'); // true
        console.log(this.repository.getTableName().toString());
      }
    
      async findOne() {
        const result = await this.repository.findOne<AppMetaData>();
        if (!result) {
          return null;
        }
        // ...
      }
    }
    

    db.ts:

    import { Sequelize } from 'sequelize-typescript';
    import { Dialect } from 'sequelize/types';
    import * as models from './models';
    
    const config = {
      db: {
        database: 'test',
        username: 'root',
        password: '',
        host: '127.0.0.1',
        dialect: 'sqlite',
      },
    };
    const DBInstance = new Sequelize(config.db.database, config.db.username, config.db.password, {
      host: config.db.host,
      dialect: config.db.dialect as Dialect,
      storage: ':memory:',
      repositoryMode: true,
      models: Object.values(models),
    });
    
    export default DBInstance;
    

    index.ts:

    import 'reflect-metadata';
    import { container } from './ioc/ioc';
    import { IAppMetaDataRepository } from './ioc/interfaces';
    import { TYPES } from './ioc/types';
    
    const appDataRepository = container.get<IAppMetaDataRepository>(TYPES.AppDataRepository);
    appDataRepository.test();
    

    Run command npx ts-node ./index.ts, execution result:

    true
    AppMetaData
    

    package versions:

    "inversify": "^5.1.1",
    "sequelize-typescript": "^1.0.0",
    "sequelize": "^5.21.3",
    "typescript": "^4.9.5",
    "reflect-metadata": "^0.1.13"