Defining Jobs
Declare immutable queue identities, codecs and payload versions.
A queue is an inert namespace. A Job identity is the literal
queue + name + version tuple; JavaScript function names are never used for
persistence or routing. Defining a Job does not resolve a Service, open a
connection or register a Worker.
import * as z from 'zod'
import { Codec, JobEncodeFailure, Queue, Retry } from 'better-effect-mq'
import { Schema as CoreSchema } from 'better-effect-schema'
import { Schema } from 'better-effect-schema/zod'
const SendEmailPayloadSchema = z.object({
to: z.email(),
messageId: z.string().min(1)
})
class SendEmailPayload extends Schema.Class<SendEmailPayload>('app/SendEmailPayload')(
SendEmailPayloadSchema
) {}
const SendEmailPayloadCodec = Codec.standardSchema({
schema: SendEmailPayload,
encode: (value) =>
CoreSchema.encode(SendEmailPayload, value).mapError(
(error) => new JobEncodeFailure({ message: error.message, code: 'schema-encode' })
)
})
const SendEmailFailure = z.object({ code: z.string().min(1) })
const Emails = Queue.define('emails')
const SendEmail = Emails.job('send-email', {
version: 1,
payload: SendEmailPayloadCodec,
result: Codec.void,
failure: Codec.standardSchema({ schema: SendEmailFailure }),
defaults: {
attempts: 5,
backoff: Retry.exponential({
initialDelayMs: 1_000,
maxDelayMs: 60_000,
maxAttempts: 5
})
},
idempotencyKey: ({ messageId }) => messageId
})Codec.standardSchema is the recommended persistence-boundary codec for a
provider-backed schema. The Zod 4 schema in this example is adapted through
better-effect-schema; its class value is encoded back to JSON before
persistence. Primitive codecs remain convenient for primitive values.
Job.PayloadInput describes the codec input; Job.Payload is the decoded value
received by a handler.
Trusted JSON escape hatch
Codec.json<T>() is an intentional simple JSON escape hatch, not the default
for boundary examples. Use it only when the value is already trusted and
JSON-safe and there is no meaningful runtime validation to perform.
Keep a new Job version when a persisted payload or result contract changes:
The following continuation uses the provider-backed setup from Defining Jobs to encode a changed payload contract.
const SendEmailV2PayloadSchema = z.object({
recipient: z.email(),
body: z.string().min(1)
})
class SendEmailV2Payload extends Schema.Class<SendEmailV2Payload>('app/SendEmailV2Payload')(
SendEmailV2PayloadSchema
) {}
const SendEmailV2PayloadCodec = Codec.standardSchema({
schema: SendEmailV2Payload,
encode: (value) =>
CoreSchema.encode(SendEmailV2Payload, value).mapError(
(error) => new JobEncodeFailure({ message: error.message, code: 'schema-encode' })
)
})
const SendEmailV2 = Emails.job('send-email', {
version: 2,
payload: SendEmailV2PayloadCodec
})A local JobRegistry can accept multiple versions for a Worker claim. It is
not a global handler registry. Version changes and upcasting are application
policy: keep old codecs available for records that are still in flight, and
remove a version only after its persisted records are retired.
Metadata is bounded string data. Do not put connections, requests, live
Error objects, lease tokens or arbitrary causes in a Job record.