Skip to main content

DI Integration

The semaphoreFactoryResolverDiFactory function creates an ISemaphoreFactoryResolver whose underlying resolver is resolved from a DI container.

The returned proxy implements both ISemaphoreFactoryResolver and ISemaphoreFactory:

  • use(adapterName?) selects an adapter on the resolved SemaphoreFactoryResolver.
  • create(key, settings) creates a semaphore through the resolver's default adapter.

The proxy is wired to the container during container.init(). Create it before calling init(); calling use() or create() before the container is initialized throws an error.

Initial configuration​

Set up the memory and Redis semaphore adapters, create the container, then register the SemaphoreFactoryResolver and wrap it with semaphoreFactoryResolverDiFactory:

1. Set up the memory semaphore adapter​

./samples/memory-semaphore-adapter.ts
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";

export const memorySemaphoreAdapter = new MemorySemaphoreAdapter();

2. Set up the Redis semaphore adapter​

./samples/redis-semaphore-adapter.ts
import { RedisSemaphoreAdapter } from "eridu-tech/semaphore/redis-semaphore-adapter";
import { Redis } from "ioredis";

export const redisSemaphoreAdapter = new RedisSemaphoreAdapter(
new Redis("YOUR_REDIS_CONNECTION_STRING"),
);

3. Create the dependency injection container​

./samples/container.ts
import { Container } from "eridu-tech/di";
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";

export const executionContext = new ExecutionContext(
new AlsExecutionContextAdapter(),
);
export const container = new Container({ executionContext });

4. Register and initialize the semaphore factory resolver​

./samples/semaphore-factory-resolver-di-factory.ts
import { SemaphoreFactoryResolver } from "eridu-tech/semaphore";
import { ProxySemaphoreFactoryResolver } from "eridu-tech/semaphore/di";
import { container } from "./container.js";
import { memorySemaphoreAdapter } from "./memory-semaphore-adapter.js";
import { redisSemaphoreAdapter } from "./redis-semaphore-adapter.js";

type Adapters = "memory" | "redis";

const semaphoreFactoryResolver = new SemaphoreFactoryResolver<Adapters>({
adapters: {
memory: memorySemaphoreAdapter,
redis: redisSemaphoreAdapter,
},
defaultAdapter: "memory",
});

await semaphoreFactoryResolver.init();

container.registerValue({
token: SemaphoreFactoryResolver,
value: semaphoreFactoryResolver,
});

// Create the proxy before container.init()
export const semaphoreFactory = new ProxySemaphoreFactoryResolver<Adapters>({
container,
resolverToken: SemaphoreFactoryResolver,
});

await container.init();

Usage​

1. Using the default adapter​

./samples/semaphore-factory-resolver-di-factory-default-adapter.ts
import { semaphoreFactory } from "./semaphore-factory-resolver-di-factory.js";

// Uses the adapter configured as the default (memory)
await semaphoreFactory
.create("shared-resource", {
limit: 2,
})
.runOrFail(async () => {
// code to run
});

2. Specifying an adapter explicitly​

./samples/semaphore-factory-resolver-di-factory-specific-adapter.ts
import { semaphoreFactory } from "./semaphore-factory-resolver-di-factory.js";

// Uses the Redis adapter
await semaphoreFactory
.use("redis")
.create("shared-resource", {
limit: 2,
})
.runOrFail(async () => {
// code to run
});

Using it with the middlewares​

Because the proxy implements ISemaphoreFactoryResolver, it can be passed straight to the semaphore middleware factories:

./samples/semaphore-factory-resolver-di-factory-with-middleware.ts
import { use } from "eridu-tech/middleware";
import { withSemaphoreFactory } from "eridu-tech/semaphore/middlewares";
import { semaphoreFactory } from "./semaphore-factory-resolver-di-factory.js";

const withSemaphore = withSemaphoreFactory(semaphoreFactory);

const processFile = async (filePath: string): Promise<void> => {
// ... process the file
};

const throttledProcess = use(
processFile,
withSemaphore.use("redis")({
key: ([filePath]) => `file-path:${filePath}`,
limit: 3,
}),
);

await throttledProcess("/data/file.json");

Further information​

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