|
2 | 2 |
|
3 | 3 | [-@2x.png)](http://loopback.io/)
|
4 | 4 |
|
5 |
| -## Installation |
| 5 | +This is a loopback 4 extension to connect dynamic datasources runtime. In todays world there can be a use case of multi tenant system in which we need the physical |
| 6 | +seperation of the databses so in loopback we need to connect to those datasources runtime and maintain and reuse those connection. |
6 | 7 |
|
7 |
| -Install Loopback4DynamicDatasourceComponent using `npm`; |
| 8 | + |
| 9 | +## Install |
8 | 10 |
|
9 | 11 | ```sh
|
10 |
| -$ [npm install | yarn add] loopback4-dynamic-datasource |
| 12 | +npm install loopback4-dynamic-datasource |
11 | 13 | ```
|
12 | 14 |
|
13 |
| -## Basic Use |
| 15 | +## Usage |
14 | 16 |
|
15 |
| -Configure and load Loopback4DynamicDatasourceComponent in the application constructor |
16 |
| -as shown below. |
| 17 | +In order to use this component into your LoopBack application, please follow below steps. |
| 18 | + |
| 19 | +- Add component to application. |
| 20 | + |
| 21 | +```ts |
| 22 | +this.component(Loopback4DynamicDatasourceComponent); |
| 23 | +``` |
| 24 | + |
| 25 | +- Now add action provider to the action based sequence (This step is not required in case of middleware based sequence) |
| 26 | + |
| 27 | +```ts |
| 28 | +export class MySequence implements SequenceHandler { |
| 29 | + |
| 30 | + constructor( |
| 31 | + ... |
| 32 | + @inject(DynamicDatasourceBindings.DYNAMIC_DATASOURCE_ACTION) |
| 33 | + private readonly setupDatasource: SetupDatasourceFn, |
| 34 | + ... |
| 35 | + ) {} |
| 36 | + |
| 37 | + async handle(context: RequestContext) { |
| 38 | + const requestTime = Date.now(); |
| 39 | + try { |
| 40 | + ... |
| 41 | + await this.setupDatasource(context); |
| 42 | + ... |
| 43 | + } catch (err) { |
| 44 | + this.reject(context, err); |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +- Now write a datasource identifier provider that is used to identify runtime which source we need to connect. |
| 52 | +In below example getting the identifier from the tenantId coming in query of the request. |
| 53 | +```ts |
| 54 | +import {DatasourceIdentifierFn} from 'loopback4-dynamic-datasouce'; |
| 55 | + |
| 56 | +export class CustomDatasourceIdentifierProvider implements Provider<DatasourceIdentifierFn> { |
| 57 | + constructor() { |
| 58 | + } |
| 59 | + |
| 60 | + value(): DatasourceIdentifierFn { |
| 61 | + return async (requestCtx) => { |
| 62 | + const tenantId = requestCtx.request.query['tenantId'] as string; |
| 63 | + return tenantId == null ? null : {id: tenantId}; |
| 64 | + }; |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | +Now bind that provider in application.ts |
| 69 | +```ts |
| 70 | +this.bind(DynamicDatasourceBindings.DATASOURCE_IDENTIFIER_PROVIDER).toProvider(CustomDatasourceIdentifierProvider); |
| 71 | +``` |
| 72 | +- Now write a datasource provider to get datasources runtime. these datasource will be created runtime on require basis |
| 73 | +```ts |
| 74 | +export class CustomDatasourceProvider implements Provider<DatasourceProviderFn> { |
| 75 | + constructor( |
| 76 | + @repository(TenantRepository) |
| 77 | + private tenantRepo: TenantRepository, |
| 78 | + ) { |
| 79 | + } |
| 80 | + |
| 81 | + value(): DatasourceProviderFn { |
| 82 | + return async (datasourceIdentifier) => { |
| 83 | + return { |
| 84 | + pgdb: async () => { |
| 85 | + const tenantData = await this.tenantRepo.findById(datasourceIdentifier.id); |
| 86 | + return new juggler.DataSource({ |
| 87 | + ...tenantData.dbConfig, |
| 88 | + }); |
| 89 | + } |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | +Now bind that provider in application.ts |
| 96 | +```ts |
| 97 | +this.bind(DynamicDatasourceBindings.DATASOURCE_PROVIDER).toProvider(CustomDatasourceProvider); |
| 98 | +``` |
| 99 | +Note:- connector of following datasource should be present in package.json like in this example we are using **loopback-connector-postgresql** |
| 100 | + |
| 101 | +Now return of this provider is an object where you can give as many keys you want but that should return juggler.Datasource |
| 102 | +This is used as the intention of connecting multiple datasource for tenant. |
| 103 | +`pgdb` this key is custom and it can be used as per your choice but your repository must use specified key in injection |
17 | 104 |
|
18 | 105 | ```ts
|
19 |
| -import {Loopback4DynamicDatasourceComponent, Loopback4DynamicDatasourceComponentOptions, DEFAULT_LOOPBACK4_DYNAMIC_DATASOURCE_OPTIONS} from 'loopback4-dynamic-datasource'; |
20 |
| -// ... |
21 |
| -export class MyApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication))) { |
22 |
| - constructor(options: ApplicationConfig = {}) { |
23 |
| - const opts: Loopback4DynamicDatasourceComponentOptions = DEFAULT_LOOPBACK4_DYNAMIC_DATASOURCE_OPTIONS; |
24 |
| - this.configure(Loopback4DynamicDatasourceComponentBindings.COMPONENT).to(opts); |
25 |
| - // Put the configuration options here |
26 |
| - }); |
27 |
| - this.component(Loopback4DynamicDatasourceComponent); |
28 |
| - // ... |
29 |
| - } |
30 |
| - // ... |
| 106 | +export class UserRepository extends DefaultCrudRepository<User, |
| 107 | + typeof User.prototype.id, |
| 108 | + UserRelations> { |
| 109 | + constructor( |
| 110 | + @inject('datasources.pgdb') dataSource: JugglerDataSource, |
| 111 | + ) { |
| 112 | + super(User, dataSource); |
| 113 | + } |
31 | 114 | }
|
32 | 115 | ```
|
| 116 | + |
| 117 | +That's all. |
| 118 | + |
| 119 | +## Feedback |
| 120 | + |
| 121 | +If you've noticed a bug or have a question or have a feature request, [search the issue tracker](https://github.com/sourcefuse/loopBack4-dynamic-datasource/issues) to see if someone else in the community has already created a ticket. |
| 122 | +If not, go ahead and [make one](https://github.com/sourcefuse/loopBack4-dynamic-datasource/issues/new/choose)! |
| 123 | +All feature requests are welcome. Implementation time may vary. Feel free to contribute the same, if you can. |
| 124 | +If you think this extension is useful, please [star](https://help.github.com/en/articles/about-stars) it. Appreciation really helps in keeping this project alive. |
| 125 | + |
| 126 | +## Contributing |
| 127 | + |
| 128 | +Please read [CONTRIBUTING.md](https://github.com/sourcefuse/loopBack4-dynamic-datasource/blob/master/.github/CONTRIBUTING.md) for details on the process for submitting pull requests to us. |
| 129 | + |
| 130 | +## Code of conduct |
| 131 | + |
| 132 | +Code of conduct guidelines [here](https://github.com/sourcefuse/loopBack4-dynamic-datasource/blob/master/.github/CODE_OF_CONDUCT.md). |
| 133 | + |
| 134 | +## License |
| 135 | + |
| 136 | +[MIT](https://github.com/sourcefuse/loopBack4-dynamic-datasource/blob/master/LICENSE) |
0 commit comments