Overview
The task builder provides a fluent interface for defining executable units of work. Tasks can have typed inputs/outputs, dependencies, middleware, and validation schemas.
import { r } from "@bluelibs/runner";
const greet = r.task("app.tasks.greet")
.inputSchema<{ name: string }>({ parse: (v) => v })
.dependencies(() => ({ logger: globals.resources.logger }))
.run(async ({ input, deps }) => {
await deps.logger.info(`Greeting ${input.name}`);
return `Hello, ${input.name}!`;
})
.build();
Methods
Defines the validation schema for task input.
.inputSchema<TInput>(schema: IValidationSchema<TInput>)
.schema<TInput>(schema: IValidationSchema<TInput>) // Alias
schema
IValidationSchema<TInput>
required
Validation schema with a parse method that validates and transforms input..inputSchema<{ email: string }>({
parse: (value) => {
if (!value.email?.includes('@')) {
throw new Error('Invalid email');
}
return value;
}
})
Returns: New builder with updated input type
resultSchema()
Defines the validation schema for task output.
.resultSchema<TOutput>(schema: IValidationSchema<TOutput>)
schema
IValidationSchema<TOutput>
required
Validation schema applied to the task’s return value..resultSchema<{ userId: string }>({
parse: (result) => {
if (!result.userId) {
throw new Error('Missing userId');
}
return result;
}
})
Returns: New builder with updated output type
run()
Defines the task’s execution logic.
.run<TInput, TOutput>(
fn: (context: TaskContext<TInput, TDeps>) => Promise<TOutput>
)
The task execution function. Receives a context object with:
input - The validated task input
deps - Resolved dependencies
runtime - Runtime instance for running other tasks/emitting events
logger - Scoped logger
journal - Execution journal for middleware communication
.run(async ({ input, deps, runtime, logger, journal }) => {
await logger.info('Starting task');
const data = await deps.database.query(input.id);
await runtime.emitEvent(dataFetched, { data });
return data;
})
Returns: New builder (required before calling build())
dependencies()
Defines dependencies to inject into the task.
.dependencies<TDeps>(
deps: TDeps | (() => TDeps),
options?: { override?: boolean }
)
deps
DependencyMap | () => DependencyMap
required
Object mapping dependency keys to resources. Can be a static object or a function..dependencies(() => ({
database: globals.resources.store,
emailService: myEmailService,
}))
When true, replaces existing dependencies. When false (default), merges with existing.
Returns: New builder with updated dependencies type
middleware()
Attaches middleware to the task.
.middleware<TMiddleware>(
mw: TaskMiddlewareAttachment[],
options?: { override?: boolean }
)
mw
TaskMiddlewareAttachment[]
required
Array of middleware attachments (middleware with configuration)..middleware([
globals.middleware.task.retry.with({ maxAttempts: 3 }),
globals.middleware.task.timeout.with({ ms: 5000 }),
customMiddleware.with({ /* config */ }),
])
When true, replaces existing middleware. When false (default), appends to existing.
Returns: New builder with middleware attached
Attaches tags for grouping and filtering.
.tags<TTags>(
tags: TagType[],
options?: { override?: boolean }
)
Array of tag definitions.const apiTag = r.tag('app.tags.api').build();
.tags([apiTag, globals.tags.debug])
When true, replaces existing tags. When false (default), appends to existing.
Returns: New builder with tags attached
Attaches metadata for documentation and tooling.
.meta<TMeta>(metadata: ITaskMeta)
Metadata object with optional title and description..meta({
title: "Send Welcome Email",
description: "Sends a welcome email to newly registered users",
})
Returns: New builder with metadata attached
throws()
Documents which errors the task may throw.
.throws(errorList: ThrowsList)
Array of error classes or error definitions.const ValidationError = r.error('app.errors.validation').build();
.throws([ValidationError, DatabaseError])
Returns: New builder with error documentation
build()
Builds and returns the final task definition.
.build(): ITask<TInput, TOutput, TDeps, TMeta, TTags, TMiddleware>
Returns: Immutable task definition ready to register
Throws: Error if run() was not called
Type Signature
interface TaskFluentBuilder<
TInput = undefined,
TOutput extends Promise<any> = Promise<any>,
TDeps extends DependencyMapType = {},
TMeta extends ITaskMeta = ITaskMeta,
TTags extends TagType[] = TagType[],
TMiddleware extends TaskMiddlewareAttachmentType[] = TaskMiddlewareAttachmentType[]
> {
id: string;
inputSchema<TNewInput>(schema: IValidationSchema<TNewInput>): TaskFluentBuilder<...>;
schema<TNewInput>(schema: IValidationSchema<TNewInput>): TaskFluentBuilder<...>;
resultSchema<TResolved>(schema: IValidationSchema<TResolved>): TaskFluentBuilder<...>;
run<TNewInput, TNewOutput>(fn: TaskRunFn<...>): TaskFluentBuilder<...>;
dependencies<TNewDeps>(deps: TNewDeps | (() => TNewDeps), options?: { override?: boolean }): TaskFluentBuilder<...>;
middleware<TNewMw>(mw: TNewMw, options?: { override?: boolean }): TaskFluentBuilder<...>;
tags<TNewTags>(tags: TNewTags, options?: { override?: boolean }): TaskFluentBuilder<...>;
meta<TNewMeta>(meta: TNewMeta): TaskFluentBuilder<...>;
throws(list: ThrowsList): TaskFluentBuilder<...>;
build(): ITask<TInput, TOutput, TDeps, TMeta, TTags, TMiddleware>;
}
Examples
Basic Task
const hello = r.task("app.tasks.hello")
.run(async () => "Hello, World!")
.build();
const greet = r.task("app.tasks.greet")
.inputSchema<{ name: string }>({
parse: (v) => {
if (!v.name) throw new Error('Name required');
return v;
}
})
.run(async ({ input }) => `Hello, ${input.name}!`)
.build();
Task with Dependencies
const createUser = r.task("app.tasks.createUser")
.inputSchema<{ email: string; name: string }>({ parse: (v) => v })
.dependencies(() => ({
database: globals.resources.store,
logger: globals.resources.logger,
}))
.run(async ({ input, deps }) => {
await deps.logger.info(`Creating user: ${input.email}`);
const user = await deps.database.insert('users', input);
return user;
})
.build();
Task with Middleware
const fetchData = r.task("app.tasks.fetchData")
.middleware([
globals.middleware.task.retry.with({ maxAttempts: 3, delayMs: 1000 }),
globals.middleware.task.timeout.with({ ms: 5000 }),
globals.middleware.task.cache.with({ ttl: 60000 }),
])
.run(async () => {
return await fetch('https://api.example.com/data');
})
.build();
Task with Everything
const processOrder = r.task("app.tasks.processOrder")
.inputSchema<{ orderId: string }>({
parse: (v) => {
if (!v.orderId) throw new Error('Order ID required');
return v;
}
})
.resultSchema<{ success: boolean; orderId: string }>({
parse: (v) => v
})
.dependencies(() => ({
database: databaseResource,
paymentService: paymentResource,
logger: globals.resources.logger,
}))
.middleware([
globals.middleware.task.retry.with({ maxAttempts: 3 }),
globals.middleware.task.timeout.with({ ms: 30000 }),
])
.tags([apiTag, criticalTag])
.meta({
title: "Process Order",
description: "Processes a customer order including payment and fulfillment",
})
.throws([PaymentError, DatabaseError])
.run(async ({ input, deps, logger }) => {
await logger.info(`Processing order ${input.orderId}`);
const order = await deps.database.getOrder(input.orderId);
await deps.paymentService.charge(order);
return { success: true, orderId: input.orderId };
})
.build();