---
head:
  - - meta
    - name: description
      content: Use @tsed/adapters-ioredis with Ts.ED.
---
# Adapters IORedis

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

## Installation

::: code-group

```sh [npm]
npm install --save @tsed/adapters-ioredis @tsed/ioredis ioredis
```

```sh [yarn]
yarn add @tsed/adapters-ioredis @tsed/ioredis ioredis
```

```sh [pnpm]
pnpm add @tsed/adapters-ioredis @tsed/ioredis ioredis
```

```sh [bun]
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:"));
  });
});
```
