nestjsx / crud

NestJs CRUD for RESTful APIs
https://github.com/nestjsx/crud/wiki
MIT License
4.05k stars 535 forks source link

Avoid duplicating Services and Controllers when using multiple connections with the same set of tables #462

Open FredericLatour opened 4 years ago

FredericLatour commented 4 years ago

Hi, I have a use case where I have multiple databases (alias connections) that have the exact same set of tables. Basically, I would like to use a specific connection depending on a "param" or a "query variable". Is there any way to have a single "CRUD" controller instead of one controller for each connection?

Basically I would have 2 services like that:

@Injectable()
export class CampaignsServiceConnA extends TypeOrmCrudService<Campaigns> {
  constructor(@InjectRepository(Campaigns, 'ConnA') repo) {
    super(repo);
  }
}
@Injectable()
export class CampaignsServiceConnB extends TypeOrmCrudService<Campaigns> {
  constructor(@InjectRepository(Campaigns, 'ConnB') repo) {
    super(repo);
  }
}

And a controller like the following:

@Crud({
  model: {
    type: Campaigns
  }
})
@Controller('campaigns/:conn')
export class CampaignsController implements CrudController<Campaigns> {
  constructor(public service: CampaignsServiceConnA OR CampaignsServiceConnB depending on the value of :conn param) {}
}

If I could even have only one parameterized CampaignService, that would be even better. Does anybody know it there is a more elegant approach than have as many services and controllers as the number of connections?

Thanks in advance

FredericLatour commented 4 years ago

Anyone? This feature makes sense considering it's possible to handle multiple typeorm connections at nestjs level. Could sponsor this feature depending on the pricing.

michaelyali commented 4 years ago

Hi, As you've mentioned, the current functionality almost totally relies on a repository. So, I don't think it's possible to do this currently. But it definitely looks like a cool possible feature. I'll think about it and hope someone will have some time to work on this too.

FredericLatour commented 4 years ago

@zMotivat0r I believe there would be an easy solution. At least not that difficult. In the current implementation, CrudController expects a TypeOrmCrudService service that expects a Repository.

This can be summarized like that:

@Crud( {model: { type: Log, },})
@Controller('log/:somevalue')
export class LogController implements CrudController<Log> {
  public service
  constructor() {
    const repo = getConnection('connectionName').getRepository<Log>(Log)
    this.service = new TypeOrmCrudService<Log>(repo)
  }
}

For simplification, I instantiated TypeOrmCrudService service directly instead of injecting it.

Now, if instead of relying solely on a Repository<T>, TypeOrmCrudService would accept the following structure :

interface IMultiRepos<T> {
  /** type for discriminating - though not really useful here */
  kind: 'MULTI_REPOS'

  /** multiple named repositories */
  namedRepos: { [key: string]:  Repository<T>} 

  /** default repository name */
  defaultRepoName: string

  /** function that receives a Request/Context object and returns the repository name that is to be used */
  getRepoName: (req) => string
}

Then I could initialize my my TypeOrmCrudService the following way

@Crud( {model: { type: Log, },})
@Controller('log/:somevalue')
export class LogController implements CrudController<Log> {
  public service
  constructor() {
    const multiRepos: IMultiRepos<Log> = {
      kind: 'MULTI_REPOS',
      namedRepos: {
        repo01: getConnection('connectionName01').getRepository<Log>(Log),
        repo02: getConnection('connectionName01').getRepository<Log>(Log),
      },
      defaultRepoName: 'repo01',
      getRepoName: (req) => req.params.somevalue
    }

    this.service = new TypeOrmCrudService<Log>(multiRepos)
    }
}

Of course, it's easy to keep backward compatibility by accepting both Repository<T> | IMultiRepos<T>.

A bit of refactoring is necessary in TypeOrmCrudService but not much, it's just a matter of delegating the proper repository depending on the Request.

What do you think?

aleho70 commented 2 years ago

I have the same request. Is there any progress on this?