Skip to content

Latest commit

 

History

History
514 lines (427 loc) · 15.6 KB

redis.md

File metadata and controls

514 lines (427 loc) · 15.6 KB

Usage

First, we need to import the RedisModule into our root module:

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:

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:

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.

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

// 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

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.
commonOptions 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.
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:// URL or 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.

Asynchronous configuration

via useFactory:

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<RedisModuleOptions> => {
        await somePromise();

        return {
          config: {
            host: 'localhost',
            port: 6379,
            password: 'authpassword'
          }
        };
      }
    })
  ]
})
export class AppModule {}

via useClass:

import { Module, Injectable } from '@nestjs/common';
import { RedisModule, RedisOptionsFactory, RedisModuleOptions } from '@liaoliaots/nestjs-redis';

@Injectable()
export class RedisConfigService implements RedisOptionsFactory {
  async createRedisOptions(): Promise<RedisModuleOptions> {
    await somePromise();

    return {
      config: {
        host: 'localhost',
        port: 6379,
        password: 'authpassword'
      }
    };
  }
}

@Module({
  imports: [
    RedisModule.forRootAsync({
      useClass: RedisConfigService
    })
  ]
})
export class AppModule {}

via extraProviders:

// an example

import { Module, ValueProvider } from '@nestjs/common';
import { RedisModule, RedisModuleOptions } from '@liaoliaots/nestjs-redis';

const MyOptionsSymbol = Symbol('options');
const MyOptionsProvider: ValueProvider<RedisModuleOptions> = {
  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.

RedisModule.forRootAsync({
  imports: [ConfigModule],
  useExisting: ConfigService
});

readyLog

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.

[Nest] 17581  - 09/16/2021, 6:03:35 PM     LOG [RedisModule] default: connected successfully to the server

Single client

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

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:

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.

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:

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.

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:

// 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:

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.

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.