Skip to content

Commit

Permalink
feat(module): implement registerAsync method
Browse files Browse the repository at this point in the history
  • Loading branch information
depyronick committed Jul 19, 2022
1 parent f7eb723 commit 59a56a6
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 20 deletions.
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,105 @@ constructor(
) { }
```

## Async Registration & Async Providers

For example, if you want to wait for the application to accept new connections until the necessary configuration settings for clickhouse are received from an asynchronous target, you can use the `registerAsync` method.

```typescript
import { Inject, Module } from '@nestjs/common';
import { ClickHouseModule } from '@depyronick/nestjs-clickhouse';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
imports: [
ConfigModule.forRoot({
envFilePath: ['.development.env'],
isGlobal: true,
}),
ClickHouseModule.registerAsync({
useFactory: (config: ConfigService) => {
return {
host: config.get('CH_HOST'),
database: config.get('CH_DB'),
password: config.get('CH_PWD'),
username: config.get('CH_USERNAME'),
};
},
inject: [ConfigService],
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
```

Then you can use the `CLICKHOUSE_ASYNC_INSTANCE_TOKEN` to inject the `ClickHouseClient` with the asynchronous configuration that you just provided.

```typescript
import {
ClickHouseClient,
CLICKHOUSE_ASYNC_INSTANCE_TOKEN,
} from '@depyronick/nestjs-clickhouse';

export class AppModule {
constructor(
@Inject(CLICKHOUSE_ASYNC_INSTANCE_TOKEN)
private readonly chWithAsyncConfig: ClickHouseClient,
) {
this.chWithAsyncConfig
.query('SELECT * FROM [TABLE] LIMIT 1')
.subscribe((row) => console.log('row', row));
}
}
```

If you want to define more than one `ClickHouseClient` using `registerAsync` method, you will need to create different modules, and inject `CLICKHOUSE_ASYNC_INSTANCE_TOKEN` into feature modules.

But you don't have to use `registerAsync` method to create asynchronous `ClickHouseClient` instances. You can also use custom providers:

```typescript
import { Inject, Module } from '@nestjs/common';
import { ClickHouseClient } from '@depyronick/nestjs-clickhouse';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
imports: [
ConfigModule.forRoot({
envFilePath: ['.development.env'],
isGlobal: true,
}),
],
controllers: [],
providers: [
{
provide: 'CH2',
useFactory: (config: ConfigService) => {
return new ClickHouseClient({
host: config.get('CH2_HOST'),
database: config.get('CH2_DB'),
password: config.get('CH2_PWD'),
username: config.get('CH2_USERNAME'),
});
},
inject: [ConfigService],
},
],
})
export class AppModule {
constructor(
@Inject('CH2')
private readonly clickhouse: ClickHouseClient,
) {
this.clickhouse
.query('SELECT * FROM [TABLE] LIMIT 1')
.subscribe((row) => console.log('row', row));
}
}
```

With custom providers, you can create as many as asynchronously loaded clients with the name you `provide`d.

## Notes

- This repository will be actively maintained and improved.
Expand Down
67 changes: 47 additions & 20 deletions lib/clickhouse.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import { DynamicModule, Module, ModuleMetadata, Provider, Type } from '@nestjs/c

import {
ClickHouseClient,
ClickHouseClientOptions as ClickHouseNodeClientOptions
ClickHouseClientOptions
} from '@depyronick/clickhouse-client';

export const CLICKHOUSE_MODULE_OPTIONS = "CLICKHOUSE_MODULE_OPTIONS";
export class ClickHouseModuleOptions extends ClickHouseClientOptions { }

export class ClickHouseModuleOptions extends ClickHouseNodeClientOptions { }
export const CLICKHOUSE_ASYNC_INSTANCE_TOKEN = 'CLICKHOUSE_INSTANCE_TOKEN';
export const CLICKHOUSE_ASYNC_MODULE_OPTIONS = 'CLICKHOUSE_MODULE_OPTIONS';

export interface ClickHouseModuleOptionsFactory {
createClickhouseOptions(): Promise<ClickHouseModuleOptions> | ClickHouseModuleOptions;
createClickHouseOptions(): Promise<ClickHouseModuleOptions> | ClickHouseModuleOptions;
}

export interface ClickHouseModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
export interface ClickHouseModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<ClickHouseModuleOptionsFactory>;
useClass?: Type<ClickHouseModuleOptionsFactory>;
useFactory?: (
Expand Down Expand Up @@ -47,37 +49,62 @@ export class ClickHouseModule {
}

static registerAsync(options: ClickHouseModuleAsyncOptions): DynamicModule {
const providers = [
...this.createAsyncProviders(options),
{
provide: CLICKHOUSE_ASYNC_INSTANCE_TOKEN,
useFactory: (options: ClickHouseModuleOptions) => {
if (!options) {
options = new ClickHouseModuleOptions();
} else {
options = Object.assign(new ClickHouseModuleOptions(), options);
}

return new ClickHouseClient(options);
},
inject: [CLICKHOUSE_ASYNC_MODULE_OPTIONS],
},
...(options.extraProviders || []),
];

return {
module: ClickHouseModule,
imports: options.imports,
providers: [
...this.createAsyncProviders(options),
...(options.extraProviders || [])
]
}
providers: providers,
exports: providers
};
}

private static createAsyncProviders(options: ClickHouseModuleAsyncOptions): Provider[] {
private static createAsyncProviders(
options: ClickHouseModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}

return [
this.createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass
}
]
useClass: options.useClass,
},
];
}

private static createAsyncOptionsProvider(options: ClickHouseModuleAsyncOptions): Provider {
private static createAsyncOptionsProvider(
options: ClickHouseModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: CLICKHOUSE_MODULE_OPTIONS,
provide: CLICKHOUSE_ASYNC_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || []
}
inject: options.inject || [],
};
}
return {
provide: CLICKHOUSE_ASYNC_MODULE_OPTIONS,
useFactory: async (optionsFactory: ClickHouseModuleOptionsFactory) =>
optionsFactory.createClickHouseOptions(),
inject: [options.useExisting || options.useClass],
};
}
}
}

0 comments on commit 59a56a6

Please sign in to comment.