Skip to main content

Configuring RateLimiter adapters

RedisRateLimiterAdapter​

To use the RedisRateLimiterAdapter, you'll need to:

  1. Install the required dependency: ioredis package.

Setup​

Connect to Redis:

./samples/redis-rate-limiter-adapter-setup.ts
import { Redis } from "ioredis";

export const database = new Redis("YOUR_REDIS_CONNECTION_STRING");

Usage​

Create the adapter:

./samples/redis-rate-limiter-adapter.ts
import { RedisRateLimiterAdapter } from "eridu-tech/rate-limiter/redis-rate-limiter-adapter";
import { database } from "./redis-rate-limiter-adapter-setup.js";

const redisRateLimiterAdapter = new RedisRateLimiterAdapter({
database,
});

Configuring backoff policy​

The type field is the only required field. All other fields are optional.

./samples/redis-rate-limiter-backoff-policy.ts
import { BACKOFFS } from "eridu-tech/backoff-policies";
import { RedisRateLimiterAdapter } from "eridu-tech/rate-limiter/redis-rate-limiter-adapter";
import { Redis } from "ioredis";
import { TimeSpan } from "eridu-tech/time-span";

const database = new Redis("YOUR_REDIS_CONNECTION_STRING");
const redisRateLimiterAdapter = new RedisRateLimiterAdapter({
database,
backoffPolicy: {
type: BACKOFFS.CONSTANT,
delay: TimeSpan.fromSeconds(1),
jitter: 0.5,
},
});

The settings are the same as backoff policies settings.

Configuring RateLimiter policy​

The type field is the only required field. All other fields are optional.

./samples/redis-rate-limiter-policy.ts
import { LIMITER_POLICIES } from "eridu-tech/rate-limiter/policies";
import { RedisRateLimiterAdapter } from "eridu-tech/rate-limiter/redis-rate-limiter-adapter";
import { Redis } from "ioredis";

const database = new Redis("YOUR_REDIS_CONNECTION_STRING");
const redisRateLimiterAdapter = new RedisRateLimiterAdapter({
database,
rateLimiterPolicy: {
type: LIMITER_POLICIES.SLIDING_WINDOW,
},
});

The settings are the same as rate-limiter policies settings.

DatabaseRateLimiterAdapter​

To use the DatabaseRateLimiterAdapter, you'll need to use IRateLimiterStorageAdapter:

  1. Creating IRateLimiterStorageAdapter:
./samples/rate-limiter-storage-adapter.ts
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";

export const rateLimiterStorageAdapter = new MemoryRateLimiterStorageAdapter();
  1. Creating DatabaseRateLimiterAdapter:
./samples/database-rate-limiter-adapter.ts
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";
import { rateLimiterStorageAdapter } from "./rate-limiter-storage-adapter.js";

const rateLimiterAdapter = new DatabaseRateLimiterAdapter({
adapter: rateLimiterStorageAdapter,
});

Configuring backoff policy​

You can use any of defined backoff policies.

./samples/database-rate-limiter-backoff-policy.ts
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";
import { constantBackoff } from "eridu-tech/backoff-policies";
import { rateLimiterStorageAdapter } from "./rate-limiter-storage-adapter.js";

const rateLimiterAdapter = new DatabaseRateLimiterAdapter({
adapter: rateLimiterStorageAdapter,
backoffPolicy: constantBackoff(),
});

Configuring RateLimiter policy​

You can use any of defined rate-limiter policies or create your own.

./samples/database-rate-limiter-policy.ts
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";
import { SlidingWindowLimiter } from "eridu-tech/rate-limiter/policies";
import { rateLimiterStorageAdapter } from "./rate-limiter-storage-adapter.js";

const rateLimiterAdapter = new DatabaseRateLimiterAdapter({
adapter: rateLimiterStorageAdapter,
rateLimiterPolicy: new SlidingWindowLimiter(),
});

NoOpRateLimiterAdapter​

The NoOpRateLimiterAdapter is a no-operation implementation, it performs no actions when called:

./samples/no-op-rate-limiter-adapter.ts
import { NoOpRateLimiterAdapter } from "eridu-tech/rate-limiter/no-op-rate-limiter-adapter";

const noOpRateLimiterAdapter = new NoOpRateLimiterAdapter();
info

The NoOpRateLimiterAdapter is useful when you want to mock out or disable your RateLimiterProvider instance.

KyselyRateLimiterStorageAdapter​

To use the KyselyRateLimiterStorageAdapter, you'll need to:

  1. Use database provider that has support for transactions.
  2. Install the required dependency: kysely package.
  3. Provide a string serializer (ISerde). We recommend using SuperJsonSerdeAdapter for this purpose.

Setup​

Create the string serializer (ISerde) and a function that creates the TransactionContext by wrapping a Kysely instance in a KyselyTransactionAdapter:

./samples/kysely-rate-limiter-adapter-setup.ts
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";
import { contextToken } from "eridu-tech/execution-context/contracts";
import { Serde } from "eridu-tech/serde";
import { SuperJsonSerdeAdapter } from "eridu-tech/serde/super-json-serde-adapter";
import { TransactionContext } from "eridu-tech/transaction-context";
import { KyselyTransactionAdapter } from "eridu-tech/transaction-context/kysely-transaction-adapter";
import type { ITransactionContext } from "eridu-tech/transaction-context/contracts";
import type { Kysely } from "kysely";

export const serde = new Serde(new SuperJsonSerdeAdapter());

const executionContext = new ExecutionContext(new AlsExecutionContextAdapter());

// `KyselyRateLimiterStorageAdapter` is transaction aware: it runs every
// rate-limiter operation through the `current` client of this context, which
// is the transaction-scoped client while a transaction is active and the base
// client otherwise.
export function createTransactionContext(
kysely: Kysely<any>,
): ITransactionContext<Kysely<any>> {
return new TransactionContext<Kysely<any>>({
token: contextToken("kysely"),
adapter: new KyselyTransactionAdapter({
database: kysely,
}),
executionContext,
});
}
info

The transactionContext setting makes the adapter transaction aware. Adapters given the same instance share the same transaction when available.

With Sqlite​

You will need to install better-sqlite3 package:

./samples/kysely-storage-sqlite.ts
import { KyselyRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/kysely-rate-limiter-storage-adapter";
import Sqlite from "better-sqlite3";
import { Kysely, SqliteDialect } from "kysely";
import {
createTransactionContext,
serde,
} from "./kysely-rate-limiter-adapter-setup.js";

const database = new Sqlite("DATABASE_NAME.db");
const kysely = new Kysely<any>({
dialect: new SqliteDialect({
database,
}),
});
const transactionContext = createTransactionContext(kysely);
export const kyselyRateLimiterStorageAdapter =
new KyselyRateLimiterStorageAdapter({
transactionContext,
serde,
});

// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselyRateLimiterStorageAdapter.init();

With Postgres​

You will need to install pg package:

./samples/kysely-storage-postgres.ts
import { KyselyRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/kysely-rate-limiter-storage-adapter";
import { Pool } from "pg";
import { Kysely, PostgresDialect } from "kysely";
import {
createTransactionContext,
serde,
} from "./kysely-rate-limiter-adapter-setup.js";

const database = new Pool({
database: "DATABASE_NAME",
host: "DATABASE_HOST",
user: "DATABASE_USER",
// DATABASE port
port: 5432,
password: "DATABASE_PASSWORD",
max: 10,
});
const kysely = new Kysely<any>({
dialect: new PostgresDialect({
pool: database,
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselyRateLimiterStorageAdapter = new KyselyRateLimiterStorageAdapter({
transactionContext,
serde,
});

// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselyRateLimiterStorageAdapter.init();

With Mysql and MariaDB​

You will need to install mysql2 package:

./samples/kysely-storage-mysql.ts
import { KyselyRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/kysely-rate-limiter-storage-adapter";
import { createPool } from "mysql2";
import { Kysely, MysqlDialect } from "kysely";
import {
createTransactionContext,
serde,
} from "./kysely-rate-limiter-adapter-setup.js";

const database = createPool({
host: "DATABASE_HOST",
// Database port
port: 3306,
database: "DATABASE_NAME",
user: "DATABASE_USER",
password: "DATABASE_PASSWORD",
connectionLimit: 10,
});
const kysely = new Kysely<any>({
dialect: new MysqlDialect({
pool: database,
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselyRateLimiterStorageAdapter = new KyselyRateLimiterStorageAdapter({
transactionContext,
serde,
});

// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselyRateLimiterStorageAdapter.init();
info

Works with both MySQL and MariaDB.

With Libsql​

You will need to install @libsql/kysely-libsql package:

./samples/kysely-storage-libsql.ts
import { KyselyRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/kysely-rate-limiter-storage-adapter";
import { LibsqlDialect } from "@libsql/kysely-libsql";
import { Kysely } from "kysely";
import {
createTransactionContext,
serde,
} from "./kysely-rate-limiter-adapter-setup.js";

const kysely = new Kysely<any>({
dialect: new LibsqlDialect({
url: "DATABASE_URL",
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselyRateLimiterStorageAdapter = new KyselyRateLimiterStorageAdapter({
transactionContext,
serde,
});

// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselyRateLimiterStorageAdapter.init();

Settings​

To clean up expired rate-limiter records, call removeAllExpired at a regular interval (for example, using a cron job):

./samples/kysely-storage-remove-all-expired.ts
import { kyselyRateLimiterStorageAdapter } from "./kysely-storage-sqlite.js";

// Remove all expired rate-limiter records manually.
await kyselyRateLimiterStorageAdapter.removeAllExpired();

MemoryRateLimiterStorageAdapter​

To use the MemoryRateLimiterStorageAdapter you only need to create instance of it:

./samples/memory-rate-limiter-storage-adapter.ts
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";

const memoryRateLimiterStorageAdapter = new MemoryRateLimiterStorageAdapter();

You can also provide an Map that will be used for storing the data in memory:

./samples/memory-rate-limiter-storage-with-map.ts
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";

const map = new Map<any, any>();
const memoryRateLimiterStorageAdapter = new MemoryRateLimiterStorageAdapter(
map,
);
info

MemoryRateLimiterStorageAdapter lets you test your app without external dependencies like Redis, ideal for local development, unit tests, integration tests and fast E2E test for the backend application.

Settings​

To clean up expired rate-limiter records, call removeAllExpired at a regular interval (for example, using a cron job):

./samples/memory-rate-limiter-remove-all-expired.ts
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";

const memoryRateLimiterStorageAdapter = new MemoryRateLimiterStorageAdapter();

// Remove all expired rate-limiter records manually.
await memoryRateLimiterStorageAdapter.removeAllExpired();

MongodbRateLimiterStorageAdapter​

To use the MongodbRateLimiterStorageAdapter, you'll need to:

  1. Use database provider that has support for transactions.
  2. Install the required dependency: mongodb package.
  3. Provide a string serializer (ISerde). We recommend using SuperJsonSerdeAdapter for this purpose.

Setup​

Connect to MongoDB, create the string serializer (ISerde) and the TransactionContext, which wraps the Db instance in a MongodbTransactionAdapter:

./samples/mongodb-rate-limiter-adapter-setup.ts
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";
import { contextToken } from "eridu-tech/execution-context/contracts";
import { Serde } from "eridu-tech/serde";
import { SuperJsonSerdeAdapter } from "eridu-tech/serde/super-json-serde-adapter";
import { TransactionContext } from "eridu-tech/transaction-context";
import { MongodbTransactionAdapter } from "eridu-tech/transaction-context/mongodb-transaction-adapter";
import { MongoClient } from "mongodb";
import type { ClientSession, Db } from "mongodb";

export const serde = new Serde(new SuperJsonSerdeAdapter());

const client = await MongoClient.connect("YOUR_MONGODB_CONNECTION_STRING");
const database = client.db("database");

// `MongodbRateLimiterStorageAdapter` is transaction aware: it runs every
// rate-limiter operation through the `current` client of this context.
export const transactionContext = new TransactionContext<Db, ClientSession>({
token: contextToken("mongodb"),
adapter: new MongodbTransactionAdapter({
client,
database,
}),
executionContext: new ExecutionContext(new AlsExecutionContextAdapter()),
});
info

The transactionContext setting makes the adapter transaction aware. Adapters given the same instance share the same transaction when available.

Usage​

Create the adapter:

./samples/mongodb-rate-limiter-storage-adapter.ts
import { MongodbRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/mongodb-rate-limiter-storage-adapter";
import {
serde,
transactionContext,
} from "./mongodb-rate-limiter-adapter-setup.js";

const mongodbRateLimiterStorageAdapter = new MongodbRateLimiterStorageAdapter({
transactionContext,
serde,
});

// You need initialize the adapter once before using it.
// During the initialization the indexes will be created
await mongodbRateLimiterStorageAdapter.init();

NoOpRateLimiterStorageAdapter​

The NoOpRateLimiterStorageAdapter is a no-operation implementation, it performs no actions when called:

./samples/no-op-rate-limiter-storage-adapter.ts
import { NoOpRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/no-op-rate-limiter-storage-adapter";

const noOpRateLimiterStorageAdapter = new NoOpRateLimiterStorageAdapter();
info

The NoOpRateLimiterStorageAdapter is useful when you want to mock out or disable your DatabaseRateLimiterAdapter instance.

Further information​

For further information refer to eridu-tech/rate-limiter API docs.