Skip to content

Adapters IORedis

@tsed/adapters-ioredis provides ioredis-backed adapter implementations for Ts.ED.

Installation

sh
npm install --save @tsed/adapters-ioredis @tsed/ioredis ioredis
sh
yarn add @tsed/adapters-ioredis @tsed/ioredis ioredis
sh
pnpm add @tsed/adapters-ioredis @tsed/ioredis ioredis
sh
bun add @tsed/adapters-ioredis @tsed/ioredis ioredis

Adapter Options

Common options used with Adapters.invokeAdapter(...):

  • connectionName: Redis connection name (default: default)
  • useHash: store payload using hash strategy (used for OIDC consumable models)
  • keyPrefix: prefix applied to all keys generated by the adapter (entity keys, index keys, OIDC keys)
typescript
import {Adapter, Adapters, AdapterModel} from "@tsed/adapters";
import {Injectable} from "@tsed/di";
import {OIRedisAdapter} from "@tsed/adapters-ioredis";
import {Property} from "@tsed/schema";

class Client implements AdapterModel {
  @Property()
  _id: string;

  @Property()
  name: string;

  @Property()
  secret: string;
}

@Injectable()
export class ClientsRepository {
  private store: Adapter<Client>;

  constructor(private adapters: Adapters) {
    this.store = this.adapters.invokeAdapter<Client>({
      adapter: OIRedisAdapter,
      collectionName: "client",
      model: Client,
      keyPrefix: "tests:e2e:run-42"
    });
  }

  async createClient(input: Pick<Client, "name" | "secret">) {
    return this.store.create(input);
  }

  async getClients() {
    return this.store.findAll();
  }

  async getClientById(id: string) {
    return this.store.findById(id);
  }

  async updateClient(id: string, patch: Partial<Client>) {
    const current = await this.store.findById(id);
    if (!current) {
      return undefined;
    }

    return this.store.update(id, {...current, ...patch});
  }

  async deleteClient(id: string) {
    return this.store.deleteById(id);
  }
}

IORedis Module Configuration

@tsed/adapters-ioredis uses connections provided by @tsed/ioredis.

Single Connection

typescript
import {Configuration} from "@tsed/di";
import "@tsed/ioredis";

@Configuration({
  ioredis: [
    {
      name: "default",
      host: "localhost",
      port: 6379
      // or url, password, tls, db...
    }
  ]
})
export class Server {}

Cluster

typescript
import {Configuration} from "@tsed/di";
import "@tsed/ioredis";

@Configuration({
  ioredis: [
    {
      name: "default",
      nodes: ["redis://localhost:7000", "redis://localhost:7001", "redis://localhost:7002"]
      // optional cluster options:
      // scaleReads: "all",
      // redisOptions: {...}
    }
  ]
})
export class Server {}

Sentinel

typescript
import {Configuration} from "@tsed/di";
import "@tsed/ioredis";

@Configuration({
  ioredis: [
    {
      name: "default",
      sentinelName: "mymaster",
      sentinels: [
        {host: "127.0.0.1", port: 26379},
        {host: "127.0.0.1", port: 26380}
      ]
      // optional redis/sentinel options:
      // password: "...",
      // redisOptions: {...}
    }
  ]
})
export class Server {}

Typical Use Case

Used for Redis-backed adapter flows requiring ioredis integration and OIDC-compatible adapter behavior.

typescript
import {OIRedisAdapter, OIDCIORedisAdapter} from "@tsed/adapters-ioredis";

Test Isolation

When tests run in parallel, prefer keyPrefix isolation:

typescript
import {randomUUID} from "node:crypto";
import {Adapters} from "@tsed/adapters";
import {OIRedisAdapter} from "@tsed/adapters-ioredis";
import {DITest, inject} from "@tsed/di";
import {Property} from "@tsed/schema";
import {TestContainersRedis} from "@tsedio/testcontainers-redis";
import {beforeAll, beforeEach, afterAll, afterEach, describe, expect, it} from "vitest";

class AuthorizationCode {
  @Property()
  _id: string;

  @Property()
  uid: string;
}

describe("OIDC adapter isolation", () => {
  let keyPrefix: string;

  beforeAll(() => TestContainersRedis.startContainer());

  beforeEach(() => {
    keyPrefix = `test:${randomUUID()}`;
    return DITest.create({
      ioredis: [
        {
          name: "default",
          ...TestContainersRedis.getRedisOptions()
        }
      ]
    });
  });

  afterEach(() => DITest.reset());
  afterAll(() => TestContainersRedis.stopContainer());

  it("isolates data per test", async () => {
    const adapter = inject(Adapters).invokeAdapter<AuthorizationCode>({
      adapter: OIRedisAdapter,
      collectionName: "AuthorizationCode",
      model: AuthorizationCode,
      keyPrefix
    });

    await adapter.create({uid: "u1"});
    const items = await adapter.findAll();
    const keys = await (adapter as any).db.keys(adapter.prefix("AuthorizationCode:*"));

    expect(items.length).toBe(1);
    expect(keys.length).toBe(1);
    expect(keys[0]).toContain(adapter.prefix("AuthorizationCode:"));
  });
});

Released under the MIT License.