Skip to main content

Lock middlewares

Initial configuration​

To begin using the lock middlewares, you'll need to create and configure a LockFactoryResolver instance:

./samples/lock.ts
import { LockFactoryResolver } from "eridu-tech/lock";
import { MemoryLockAdapter } from "eridu-tech/lock/memory-lock-adapter";
import { RedisLockAdapter } from "eridu-tech/lock/redis-lock-adapter";
import { Redis } from "ioredis";

export const lockFactoryResolver = new LockFactoryResolver({
adapters: {
memory: new MemoryLockAdapter(),
redis: new RedisLockAdapter(new Redis("YOUR_REDIS_CONNECTION_STRING")),
},
defaultAdapter: "memory",
});

await lockFactoryResolver.init();

withLockFactory middleware​

The Lock middleware wraps function calls with a distributed lock, ensuring mutual exclusion across processes. Before executing the wrapped function, a lock is acquired on a key derived from the function's arguments. If another process already holds the lock, the call waits (or fails immediately for non-blocking locks) until the lock is released.

Usage​

./samples/with-lock.ts
import { withLockFactory } from "eridu-tech/lock/middlewares";
import { use } from "eridu-tech/middleware";
import { lockFactoryResolver } from "./lock.js";

const withLock = withLockFactory(lockFactoryResolver);

const processJob = async (jobId: string): Promise<void> => {
// Critical section — only one process should execute this at a time
// ... process the job
};

// Wrap with distributed lock using the default adapter (`memory`)
const safeProcess = use(
processJob,
withLock({
key: ([jobId]) => `job:${jobId}`,
}),
);

// Wrap with distributed lock using a specific adapter (`redis`)
const safeProcessOnRedis = use(
processJob,
withLock.use("redis")({
key: ([jobId]) => `job:${jobId}`,
}),
);

await safeProcess("job-123"); // Acquires lock, processes, releases lock
await safeProcessOnRedis("job-456"); // Uses the Redis adapter

Calling withLock() directly uses the resolver's default adapter (memory). Use withLock.use("redis") to run the wrapped function through a specific registered adapter.

info

Here is a complete list of settings for the withLock function.

Settings​

OptionTypeDescription
keyInvocable<TParameters, string>A function that produces the lock key from the wrapped function's arguments. The lock is acquired on this key, ensuring mutual exclusion across processes for the same key
lockIdInvocable<TParameters, string>Optional function that produces a unique identifier for the current lock acquisition attempt. Defaults to a UUID (v4)
ttlITimeSpan | nullTime-to-live for the lock. null means the lock never expires automatically; if omitted the factory's default TTL is used

withHttpLockError middleware​

The withHttpLockError middleware converts every lock error thrown by the wrapped endpoint or downstream middleware into an HttpError, so the request fails with a meaningful status code instead of a generic 500. Errors that are not lock errors are rethrown untouched, and the original error is preserved as the cause.

Usage​

./samples/with-http-lock-error.ts
import { FailedAcquireLockError } from "eridu-tech/lock/contracts";
import { withHttpLockError } from "eridu-tech/lock/middlewares";
import { HttpRouter, defaultHttpRouterAdapter } from "eridu-tech/http-router";

const router = new HttpRouter({
router: defaultHttpRouterAdapter(),
});

router.use(withHttpLockError());

router.endpoint({
url: "/jobs/1",
method: ["POST"],
handler: async () => {
// A 409 response when another caller owns the lock
throw FailedAcquireLockError.create("job:1");
},
});

Status codes​

ErrorsStatus
FailedAcquireLockError, FailedRefreshLockError, FailedReleaseLockError409
UnableToAcquireLockError, UnableToReleaseLockError, UnableToForceReleaseLockError, UnableToRefreshLockError, UnableToGetStateLockError503

A 409 response means the lock is owned by another caller or is no longer valid. A 503 response means the lock adapter failed, so the same request can be retried later. The original error is preserved as the cause, and no identifiers are exposed in the response body.

Further information​

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