Skip to content

Handlers

Handlers are executable units that process queries or commands. A handler is the entry point for a business operation.

Defining Handlers

Create handlers that implement the Handler<QueryContract<...>> interface with a unique static key and executor function:

typescript
import { type Handler, type QueryContract } from "awilixify";
import type { UserModuleDeps } from "./user.module";

// Define payload and response types
type Payload = { userId: string };
type Response = { id: string; role: "admin" | "user" };

export class GetUserQueryHandler implements Handler<
  GetUserQueryHandler["contract"]
> {
  static readonly key = "users/get-user";
  declare readonly contract: QueryContract<
    typeof GetUserQueryHandler.key,
    Payload,
    Response
  >;

  constructor(private readonly userService: UserModuleDeps["userService"]) {}

  async executor(payload: Payload): Promise<Response> {
    return this.userService.findById(payload.userId);
  }
}

Registering Handlers in Modules

Add query handlers to the module's queryHandlers array. Include them in the ModuleDef type for full mediator type safety:

typescript
import { createModule, type ModuleDef } from "awilixify";

type UserModuleDef = ModuleDef<{
  providers: {
    userService: UserService;
  };
  queryHandlers: [typeof GetUserQueryHandler];
}>;

export type Deps = UserModuleDef["deps"];

export const UserModule = createModule<UserModuleDef>({
  name: "UserModule",
  providers: {
    userService: UserService,
  },
  queryHandlers: [GetUserQueryHandler],
});

Handlers are regular DI-resolved classes

Handlers are resolved through the same DI container as providers and controllers. That means DI options (like lifetime) are also valid for handlers:

typescript
import { Lifetime } from "awilix";

export const UserModule = createModule<UserModuleDef>({
  name: "UserModule",
  providers: {
    userService: UserService,
  },
  queryHandlers: [
    {
      useClass: GetUserQueryHandler,
      lifetime: Lifetime.SCOPED,
    },
  ],
});