diff --git a/README.md b/README.md index 82c03957..a4a04d08 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

Nest Redis Module

- Redis(ioredis) module for Nest framework (node.js). + Redis(ioredis & node-redis) module for Nest framework (node.js).
Explore the docs ยป
@@ -76,8 +76,12 @@ This lib requires **Node.js >=16.13.0**, **NestJS ^10.0.0**, **ioredis ^5.0.0**. - If you depend on **ioredis 5** & **NestJS 10**, please use version **10** of the lib. - If you depend on **ioredis 5** & **NestJS 9**, please use version **9** of the lib. -- If you depend on **ioredis 4**, please use [version 7](https://github.com/liaoliaots/nestjs-redis/tree/v7.0.0) of the lib. - If you depend on **ioredis 5**, **NestJS 7** or **8**, please use [version 8](https://github.com/liaoliaots/nestjs-redis/tree/v8.2.2) of the lib. +- If you depend on **ioredis 4**, please use [version 7](https://github.com/liaoliaots/nestjs-redis/tree/v7.0.0) of the lib. + +### Node-Redis + +If you prefre [node-redis](https://github.com/redis/node-redis), check out [this guide](), but it is in working progress. ### Installation diff --git a/docs/v9/cluster.md b/docs/v9/cluster.md new file mode 100644 index 00000000..635f078a --- /dev/null +++ b/docs/v9/cluster.md @@ -0,0 +1,359 @@ +## Usage + +**First**, we need to import the `ClusterModule` into our root module: + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + }) + ] +}) +export class AppModule {} +``` + +**Now**, we can use cluster in two ways. + +via decorator: + +```ts +import { Injectable } from '@nestjs/common'; +import { InjectCluster, DEFAULT_CLUSTER_NAMESPACE } from '@liaoliaots/nestjs-redis'; +import { Cluster } from 'ioredis'; + +@Injectable() +export class AppService { + constructor( + @InjectCluster() private readonly cluster: Cluster // or // @InjectCluster(DEFAULT_CLUSTER_NAMESPACE) private readonly cluster: Cluster + ) {} + + async set() { + return await this.cluster.set('key', 'value', 'EX', 10); + } +} +``` + +via service: + +```ts +import { Injectable } from '@nestjs/common'; +import { ClusterService, DEFAULT_CLUSTER_NAMESPACE } from '@liaoliaots/nestjs-redis'; +import { Cluster } from 'ioredis'; + +@Injectable() +export class AppService { + private readonly cluster: Cluster; + + constructor(private readonly clusterService: ClusterService) { + this.cluster = this.clusterService.getClient(); + // or + // this.cluster = this.clusterService.getClient(DEFAULT_CLUSTER_NAMESPACE); + } + + async set() { + return await this.cluster.set('key', 'value', 'EX', 10); + } +} +``` + +> HINT: By default, the `ClusterModule` is a [**Global module**](https://docs.nestjs.com/modules#global-modules). + +> HINT: If you don't set the `namespace` for a client, its namespace is set to `"default"`. Please note that you shouldn't have multiple client without a namespace, or with the same namespace, otherwise they will get overridden. + +## Configuration + +### [ClusterModuleOptions](/packages/redis/lib/cluster/interfaces/cluster-module-options.interface.ts) + +| Name | Type | Default | Required | Description | +| ----------- | -------------------------------------------------- | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| closeClient | `boolean` | `true` | `false` | If set to `true`, all clients will be closed automatically on nestjs application shutdown. To use `closeClient`, you **must enable listeners** by calling `app.enableShutdownHooks()`. [Read more about the application shutdown.](https://docs.nestjs.com/fundamentals/lifecycle-events#application-shutdown) | +| readyLog | `boolean` | `false` | `false` | If set to `true`, then ready logging will be displayed when the client is ready. | +| errorLog | `boolean` | `true` | `false` | If set to `true`, then errors that occurred while connecting will be displayed by the built-in logger. | +| config | `ClusterClientOptions` \| `ClusterClientOptions`[] | `undefined` | `true` | Used to specify single or multiple clients. | + +### [ClusterClientOptions](/packages/redis/lib/cluster/interfaces/cluster-module-options.interface.ts) + +| Name | Type | Default | Required | Description | +| ---------------------- | ---------------------------------------------------------------- | ----------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| namespace | `string` \| `symbol` | `'default'` | `false` | Client name. If client name is not given then it will be called "default". Different clients must have different names. You can import `DEFAULT_CLUSTER_NAMESPACE` to use it. | +| nodes | `{ host?: string; port?: number }[]` \| `string[]` \| `number[]` | `undefined` | `true` | List of cluster nodes. | +| onClientCreated | `function` | `undefined` | `false` | Function to be executed as soon as the client is created. | +| **...** ClusterOptions | `ClusterOptions` | - | `false` | Inherits from [ClusterOptions](https://luin.github.io/ioredis/interfaces/ClusterOptions.html). | + +### Asynchronous configuration + +via `useFactory`: + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule, ClusterModuleOptions } from '@liaoliaots/nestjs-redis'; +import { ConfigService, ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + ClusterModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (configService: ConfigService): Promise => { + await somePromise(); + + return { + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + }; + } + }) + ] +}) +export class AppModule {} +``` + +via `useClass`: + +```ts +import { Module, Injectable } from '@nestjs/common'; +import { ClusterModule, ClusterOptionsFactory, ClusterModuleOptions } from '@liaoliaots/nestjs-redis'; + +@Injectable() +export class ClusterConfigService implements ClusterOptionsFactory { + async createClusterOptions(): Promise { + await somePromise(); + + return { + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + }; + } +} + +@Module({ + imports: [ + ClusterModule.forRootAsync({ + useClass: ClusterConfigService + }) + ] +}) +export class AppModule {} +``` + +via `extraProviders`: + +```ts +// just a simple example + +import { Module, ValueProvider } from '@nestjs/common'; +import { ClusterModule, ClusterModuleOptions } from '@liaoliaots/nestjs-redis'; + +const MyOptionsSymbol = Symbol('options'); +const MyOptionsProvider: ValueProvider = { + provide: MyOptionsSymbol, + useValue: { + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + } +}; + +@Module({ + imports: [ + ClusterModule.forRootAsync({ + useFactory(options: ClusterModuleOptions) { + return options; + }, + inject: [MyOptionsSymbol], + extraProviders: [MyOptionsProvider] + }) + ] +}) +export class AppModule {} +``` + +... or via `useExisting`, if you wish to use an existing configuration provider imported from a different module. + +```ts +ClusterModule.forRootAsync({ + imports: [ConfigModule], + useExisting: ConfigService +}); +``` + +### readyLog + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + readyLog: true, + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + }) + ] +}) +export class AppModule {} +``` + +The `ClusterModule` will display a message when `CLUSTER INFO` reporting the cluster is able to receive commands. + +```sh +[Nest] 18886 - 09/16/2021, 6:19:56 PM LOG [ClusterModule] default: connected successfully to the server +``` + +### Single client + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + + // or with URL + // nodes: ['redis://:authpassword@localhost:16380'] + } + }) + ] +}) +export class AppModule {} +``` + +### Multiple clients + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + config: [ + { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + }, + { + namespace: 'cluster2', + nodes: [{ host: 'localhost', port: 16480 }], + redisOptions: { password: 'authpassword' } + } + ] + }) + ] +}) +export class AppModule {} +``` + +with URL: + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + config: [ + { + nodes: ['redis://:authpassword@localhost:16380'] + }, + { + namespace: 'cluster2', + nodes: ['redis://:authpassword@localhost:16480'] + } + ] + }) + ] +}) +export class AppModule {} +``` + +### onClientCreated + +For example, we can listen to some events of the cluster instance. + +```ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + ClusterModule.forRoot({ + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' }, + onClientCreated(client) { + client.on('error', err => {}); + client.on('ready', () => {}); + } + } + }) + ] +}) +export class AppModule {} +``` + +### Non-Global + +By default, the `ClusterModule` is a **Global module**, `ClusterService` and all cluster instances are registered in the global scope. Once defined, they're available everywhere. + +You can change this behavior by `isGlobal` parameter: + +```ts +// cats.module.ts +import { Module } from '@nestjs/common'; +import { ClusterModule } from '@liaoliaots/nestjs-redis'; +import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller'; + +@Module({ + imports: [ + ClusterModule.forRoot( + { + config: { + nodes: [{ host: 'localhost', port: 16380 }], + redisOptions: { password: 'authpassword' } + } + }, + false // <-- providers are registered in the module scope + ) + ], + providers: [CatsService], + controllers: [CatsController] +}) +export class CatsModule {} +``` + +### Testing + +This package exposes `getClusterToken()` function that returns an internal injection token based on the provided context. Using this token, you can provide a mock implementation of the cluster instance using any of the standard custom provider techniques, including `useClass`, `useValue`, and `useFactory`. + +```ts +import { Test, TestingModule } from '@nestjs/testing'; +import { getRedisToken } from '@liaoliaots/nestjs-redis'; + +const module: TestingModule = await Test.createTestingModule({ + providers: [{ provide: getClusterToken('namespace'), useValue: mockedInstance }, YourService] +}).compile(); +``` + +A working example is available [here](/sample). diff --git a/docs/v9/examples.md b/docs/v9/examples.md new file mode 100644 index 00000000..20081c0b --- /dev/null +++ b/docs/v9/examples.md @@ -0,0 +1,57 @@ +## Redis + +### Sentinel + +| name | address | port | password | +| ---------------------- | --------- | ---- | ----------- | +| master | localhost | 6380 | my_password | +| slave1 | localhost | 6480 | my_password | +| slave2 | localhost | 6481 | my_password | +| sentinel1 (`mymaster`) | localhost | 7380 | sentinel | +| sentinel2 (`mymaster`) | localhost | 7381 | sentinel | + +> INFO: Read more about ioredis sentinel [here](https://github.com/luin/ioredis#sentinel). + +> HINT: When using Sentinel in Master-Slave setup, if you want to set the passwords for Master and Slave nodes, consider having the same password for them ([#7292](https://github.com/redis/redis/issues/7292)). + +```ts +// app.module.ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + readyLog: true, + commonOptions: { + name: 'mymaster', + sentinels: [ + { + host: 'localhost', + port: 7380 + }, + { + host: 'localhost', + port: 7381 + } + ], + sentinelPassword: 'sentinel', + password: 'my_password' + }, + config: [ + { + // get master node from the sentinel group + role: 'master', + namespace: "I'm master" + }, + { + // get a random slave node from the sentinel group + role: 'slave', + namespace: "I'm slave" + } + ] + }) + ] +}) +export class AppModule {} +``` diff --git a/docs/v9/redis.md b/docs/v9/redis.md new file mode 100644 index 00000000..9db5898c --- /dev/null +++ b/docs/v9/redis.md @@ -0,0 +1,514 @@ +## Usage + +**First**, we need to import the `RedisModule` into our root module: + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }) + ] +}) +export class AppModule {} +``` + +**Now**, we can use redis in two ways. + +via decorator: + +```ts +import { Injectable } from '@nestjs/common'; +import { InjectRedis, DEFAULT_REDIS_NAMESPACE } from '@liaoliaots/nestjs-redis'; +import Redis from 'ioredis'; + +@Injectable() +export class AppService { + constructor( + @InjectRedis() private readonly redis: Redis // or // @InjectRedis(DEFAULT_REDIS_NAMESPACE) private readonly redis: Redis + ) {} + + async set() { + return await this.redis.set('key', 'value', 'EX', 10); + } +} +``` + +via service: + +```ts +import { Injectable } from '@nestjs/common'; +import { RedisService, DEFAULT_REDIS_NAMESPACE } from '@liaoliaots/nestjs-redis'; +import Redis from 'ioredis'; + +@Injectable() +export class AppService { + private readonly redis: Redis; + + constructor(private readonly redisService: RedisService) { + this.redis = this.redisService.getClient(); + // or + // this.redis = this.redisService.getClient(DEFAULT_REDIS_NAMESPACE); + } + + async set() { + return await this.redis.set('key', 'value', 'EX', 10); + } +} +``` + +> HINT: By default, the `RedisModule` is a [**Global module**](https://docs.nestjs.com/modules#global-modules). + +> HINT: If you don't set the `namespace` for a client, its namespace is set to `"default"`. Please note that you shouldn't have multiple client without a namespace, or with the same namespace, otherwise they will get overridden. + +### Use with other libs + +```ts +// an example +import { Module } from '@nestjs/common'; +import { RedisModule, RedisService } from '@liaoliaots/nestjs-redis'; +import { ThrottlerModule } from '@nestjs/throttler'; +import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }), + ThrottlerModule.forRootAsync({ + useFactory(redisService: RedisService) { + const redis = redisService.getClient(); + return { ttl: 60, limit: 600, storage: new ThrottlerStorageRedisService(redis, 1000) }; + }, + inject: [RedisService] + }) + ] +}) +export class AppModule {} +``` + +## Configuration + +### [RedisModuleOptions](/packages/redis/lib/redis/interfaces/redis-module-options.interface.ts) + +| Name | Type | Default | Required | Description | +| ------------- | ---------------------------------------------------------------------- | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| closeClient | `boolean` | `true` | `false` | If set to `true`, all clients will be closed automatically on nestjs application shutdown. To use `closeClient`, you **must enable listeners** by calling `app.enableShutdownHooks()`. [Read more about the application shutdown.](https://docs.nestjs.com/fundamentals/lifecycle-events#application-shutdown) | +| commonOptions | [RedisOptions](https://luin.github.io/ioredis/index.html#RedisOptions) | `undefined` | `false` | Common options to be passed to each client. | +| readyLog | `boolean` | `false` | `false` | If set to `true`, then ready logging will be displayed when the client is ready. | +| errorLog | `boolean` | `true` | `false` | If set to `true`, then errors that occurred while connecting will be displayed by the built-in logger. | +| config | `RedisClientOptions` \| `RedisClientOptions`[] | `undefined` | `false` | Used to specify single or multiple clients. | + +### [RedisClientOptions](/packages/redis/lib/redis/interfaces/redis-module-options.interface.ts) + +| Name | Type | Default | Required | Description | +| -------------------- | -------------------- | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| namespace | `string` \| `symbol` | `'default'` | `false` | Client name. If client name is not given then it will be called `"default"`. Different clients must have different names. You can import `DEFAULT_REDIS_NAMESPACE` to use it. | +| url | `string` | `undefined` | `false` | URI scheme to be used to specify connection options as a [redis://](https://www.iana.org/assignments/uri-schemes/prov/redis) URL or [rediss://](https://www.iana.org/assignments/uri-schemes/prov/rediss) URL. | +| path | `string` | `undefined` | `false` | Path to be used for Unix domain sockets. | +| onClientCreated | `function` | `undefined` | `false` | Function to be executed as soon as the client is created. | +| **...** RedisOptions | `RedisOptions` | - | `false` | Inherits from [RedisOptions](https://luin.github.io/ioredis/index.html#RedisOptions). | + +### Asynchronous configuration + +via `useFactory`: + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule, RedisModuleOptions } from '@liaoliaots/nestjs-redis'; +import { ConfigService, ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + RedisModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (configService: ConfigService): Promise => { + await somePromise(); + + return { + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }; + } + }) + ] +}) +export class AppModule {} +``` + +via `useClass`: + +```ts +import { Module, Injectable } from '@nestjs/common'; +import { RedisModule, RedisOptionsFactory, RedisModuleOptions } from '@liaoliaots/nestjs-redis'; + +@Injectable() +export class RedisConfigService implements RedisOptionsFactory { + async createRedisOptions(): Promise { + await somePromise(); + + return { + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }; + } +} + +@Module({ + imports: [ + RedisModule.forRootAsync({ + useClass: RedisConfigService + }) + ] +}) +export class AppModule {} +``` + +via `extraProviders`: + +```ts +// an example + +import { Module, ValueProvider } from '@nestjs/common'; +import { RedisModule, RedisModuleOptions } from '@liaoliaots/nestjs-redis'; + +const MyOptionsSymbol = Symbol('options'); +const MyOptionsProvider: ValueProvider = { + provide: MyOptionsSymbol, + useValue: { + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + } +}; + +@Module({ + imports: [ + RedisModule.forRootAsync({ + useFactory(options: RedisModuleOptions) { + return options; + }, + inject: [MyOptionsSymbol], + extraProviders: [MyOptionsProvider] + }) + ] +}) +export class AppModule {} +``` + +... or via `useExisting`, if you wish to use an existing configuration provider imported from a different module. + +```ts +RedisModule.forRootAsync({ + imports: [ConfigModule], + useExisting: ConfigService +}); +``` + +### readyLog + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + readyLog: true, + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }) + ] +}) +export class AppModule {} +``` + +The `RedisModule` will display a message when the server reports that it is ready to receive commands. + +```sh +[Nest] 17581 - 09/16/2021, 6:03:35 PM LOG [RedisModule] default: connected successfully to the server +``` + +### Single client + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + + // or with URL + // url: 'redis://:authpassword@localhost:6379/0' + } + }) + ] +}) +export class AppModule {} +``` + +### Multiple clients + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: [ + { + host: 'localhost', + port: 6379, + password: 'authpassword' + }, + { + namespace: 'master2', + host: 'localhost', + port: 6380, + password: 'authpassword' + } + ] + }) + ] +}) +export class AppModule {} +``` + +with URL: + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: [ + { + url: 'redis://:authpassword@localhost:6379/0' + }, + { + namespace: 'master2', + url: 'redis://:authpassword@localhost:6380/0' + } + ] + }) + ] +}) +export class AppModule {} +``` + +### commonOptions + +**In some cases**, you can move the same config of multiple clients to `commonOptions`. + +> HINT: The `commonOptions` option works only with multiple clients. + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + commonOptions: { + enableAutoPipelining: true + }, + config: [ + { + host: 'localhost', + port: 6379, + password: 'authpassword' + }, + { + namespace: 'master2', + host: 'localhost', + port: 6380, + password: 'authpassword' + } + ] + }) + ] +}) +export class AppModule {} +``` + +You can also override the `commonOptions`: + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + commonOptions: { + enableAutoPipelining: true + }, + config: [ + { + host: 'localhost', + port: 6379, + password: 'authpassword' + }, + { + namespace: 'master2', + host: 'localhost', + port: 6380, + password: 'authpassword', + enableAutoPipelining: false + } + ] + }) + ] +}) +export class AppModule {} +``` + +### onClientCreated + +For example, we can listen to some events of the redis instance. + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: { + host: 'localhost', + port: 6379, + password: 'authpassword', + onClientCreated(client) { + client.on('error', err => {}); + client.on('ready', () => {}); + } + } + }) + ] +}) +export class AppModule {} +``` + +### Non-Global + +By default, the `RedisModule` is a **Global module**, `RedisService` and all redis instances are registered in the global scope. Once defined, they're available everywhere. + +You can change this behavior by `isGlobal` parameter: + +```ts +// cats.module.ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; +import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller'; + +@Module({ + imports: [ + RedisModule.forRoot( + { + config: { + host: 'localhost', + port: 6379, + password: 'authpassword' + } + }, + false // <-- providers are registered in the module scope + ) + ], + providers: [CatsService], + controllers: [CatsController] +}) +export class CatsModule {} +``` + +### Unix domain socket + +**1**, open your **_redis.conf_** in a text editor and scroll down until you get to the unix socket section: + +``` +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /run/redis.sock +# unixsocketperm 700 +``` + +**2**, uncomment these lines, now look like this: + +``` +# create a unix domain socket +unixsocket /run/redis.sock +# set permissions to 777 +unixsocketperm 777 +``` + +**3**, save and exit, then restart your redis server. + +**4**, let's setup our application: + +```ts +import { Module } from '@nestjs/common'; +import { RedisModule } from '@liaoliaots/nestjs-redis'; + +@Module({ + imports: [ + RedisModule.forRoot({ + config: { + path: '/run/redis.sock' + } + }) + ] +}) +export class AppModule {} +``` + +And there we go. + +### Testing + +This package exposes `getRedisToken()` function that returns an internal injection token based on the provided context. Using this token, you can provide a mock implementation of the redis instance using any of the standard custom provider techniques, including `useClass`, `useValue`, and `useFactory`. + +```ts +import { Test, TestingModule } from '@nestjs/testing'; +import { getRedisToken } from '@liaoliaots/nestjs-redis'; + +const module: TestingModule = await Test.createTestingModule({ + providers: [{ provide: getRedisToken('namespace'), useValue: mockedInstance }, YourService] +}).compile(); +``` + +A working example is available [here](/sample).