Feature Slices
As your application grows, defining all job types and processors in a single file becomes unwieldy. Feature slices let you split them by domain — each slice owns its type definitions and processor handlers, composed together at the application level.
Defining a Slice
Section titled “Defining a Slice”A slice consists of two files: definitions and processors.
Directorysrc/
- slice-orders-definitions.ts
- slice-orders-processors.ts
- slice-notifications-definitions.ts
- slice-notifications-processors.ts
- client.ts
- index.ts
Definitions declare the job types for a feature:
import { defineJobTypes } from "queuert";
export const orderJobTypes = defineJobTypes<{ "orders.create": { entry: true; input: { userId: string }; output: { orderId: string } }; "orders.fulfill": { input: { orderId: string }; output: { fulfilled: boolean } };}>();Processors implement the handlers, typed against the slice’s definitions:
import { createProcessors } from "queuert";import { client } from "./client.js";import { orderJobTypes } from "./slice-orders-definitions.js";
export const orderProcessors = createProcessors({ client, jobTypes: orderJobTypes, processors: { "orders.create": { attemptHandler: async ({ job, complete }) => complete(async ({ continueWith }) => continueWith({ typeName: "orders.fulfill", input: { orderId: "123" } }), ), }, "orders.fulfill": { attemptHandler: async ({ job, complete }) => complete(async () => ({ fulfilled: true })), }, },});createProcessors type-checks each handler against the slice’s own definitions (plus any external defs it declares), then returns a Processors that’s plugged into createInProcessWorker.
Composing Slices
Section titled “Composing Slices”At the application level, pass arrays of slices directly to createClient and createInProcessWorker:
import { createClient, createInProcessWorker } from "queuert";import { orderJobTypes } from "./slice-orders-definitions.js";import { orderProcessors } from "./slice-orders-processors.js";import { notificationJobTypes } from "./slice-notifications-definitions.js";import { notificationProcessors } from "./slice-notifications-processors.js";
const client = await createClient({ stateAdapter, notifyAdapter, jobTypes: [orderJobTypes, notificationJobTypes],});
const worker = await createInProcessWorker({ client, processors: [orderProcessors, notificationProcessors],});Both fields accept a single slice or an array of slices. When an array is passed, duplicate type/processor keys are detected:
createClient/jobTypes— overlapping type names produce a TypeScript error at compile time; validated registries with overlappinggetTypeNames()throwDuplicateJobTypeErrorat runtime.createInProcessWorker/processors— overlapping processor keys produce a TypeScript error at compile time and throwDuplicateJobTypeErrorat runtime.
Cross-Slice References
Section titled “Cross-Slice References”When a slice needs to reference job types from another slice — for example, declaring a blocker from the notifications domain — use the optional TExternal type parameter on defineJobTypes:
import { type JobTypeDefinitions, defineJobTypes } from "queuert";import { type notificationJobTypes } from "./slice-notifications-definitions.js";
export const orderJobTypes = defineJobTypes< { "orders.place": { entry: true; input: { userId: string }; continueWith: { typeName: "orders.confirm" }; }; "orders.confirm": { input: { orderId: string }; output: { confirmed: boolean }; blockers: [{ typeName: "notifications.send" }]; }; }, // External types — available for blocker reference validation, not owned by this slice JobTypeDefinitions<typeof notificationJobTypes>>();T(first parameter) = owned definitions — these become the registry’s phantom typeTExternal(second parameter) = read-only reference context, defaults toRecord<never, never>blockersvalidates against entry types inT & TExternal- The registry’s phantom type remains
Tonly —TExternaltypes are not included
This eliminates the need for “workflow slices” that duplicate type definitions just to make blocker references type-check. When slices are passed as an array to createClient, all references resolve against the full set of definitions.
When writing processors that reference types from another slice, nothing special is needed — the client already exposes every slice’s types, so continueWith and blockers resolve against the full set.
Naming Convention
Section titled “Naming Convention”orders.create-orderorders.fulfill-ordernotifications.send-notificationSee Also
Section titled “See Also”- Utilities — API reference for
defineJobTypes,createJobTypes,createProcessors - Chain Patterns — continuation references and patterns
- showcase-slices example — full runnable example