Trigger.dev
Configure Trigger.dev for an Orders CSV task and check worker publication.
Trigger.dev needs a project, environment API key, and published worker generation alongside the RELKIT job binding. This page shows the RELKIT side and the exact registration checks; it is a compatibility guide, not a passing setup tutorial. For a verified local run today, use Inngest or Effect MQ.
Configure an existing app
The --jobs trigger-docker creation tuple is currently rejected before staging because its native
and timing evidence is incomplete. Begin with an existing project and install the selected adapter,
native SDK and Docker materializer before applying the configuration below:
From this guide’s application/package root, install only its selected requirements:
bun add @relkit/docker@0.7.12 @relkit/local@0.7.12 @relkit/trigger@0.8.1 @trigger.dev/sdk@4.7.2
Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
This aligns declarations with the adapter’s current SDK contract. It does not repair the unresolved provider/server compatibility or publish a worker.
jobs.default connects the provider; defaults.jobs selects it. Extend the existing config while
retaining its own ID and server settings.
/** Declares task application defaults with inspection disabled until selected. */
import { defineApp } from "@relkit/app/config";
import env from "@app/platform/env.js";
import { docker } from "@relkit/docker";
import { trigger } from "@relkit/trigger";
export default defineApp({
id: "relkit-trigger",
env,
server: { port: 3000, maxBodyBytes: 1_048_576 },
telemetry: { redaction: { mode: "development-redacted", maxBytes: 65_536 } },
jobs: { default: docker(trigger()) },
defaults: { jobs: "default" },
});
What provider setup is missing
Trigger’s official Docker Compose setup
runs a webapp, database, Redis, and worker supervisor. After bringing that stack up, an
operator creates a project, obtains a development environment API key, configures the SDK
base URL and key, and deploys a worker containing the task before triggering a run. These
are provider-side steps; the RELKIT binding above does not replace them. The generated
trigger-docker recipe instead starts a pinned webapp image with PostgreSQL and a RELKIT
worker, generates a random projectRef and secretKey, and passes them to the SDK. It does
not create those credentials in Trigger.dev or publish a worker generation. Its pinned
webapp also fails its current environment and port configuration. Running the official
Compose stack separately does not make this generated RELKIT profile compatible by
itself. The adapter currently imports @trigger.dev/sdk/v3; Trigger’s
self-hosting guidance says 4.5.0 is the
last server release supporting v3 tasks. The server, SDK, task publication, and project
credentials must be aligned together. Do not paste a production Trigger key into the
generated source or assume that local up completing means a task is registered there.
Add the Orders boundary
These instructions extend an existing application; they do not create a jobs starter.
For a grouped minimal app, create src/services/orders/functions, tasks and jobs.
Add the following boundary and health function. In a compatible flat app, use its
existing src/orders/ root consistently instead. If Orders already exists, extend
its public functions rather than replacing the boundary.
/** Declares the orders service boundary and its public members. */
import { defineService } from "@relkit/app/services";
import exportOrders from "@app/services/orders/jobs/export-orders.job.js";
import exportOrdersTask from "@app/services/orders/tasks/export-orders.task.js";
import health from "@app/services/orders/functions/health.function.js";
export default defineService({
functions: { health },
tasks: { exportOrdersTask },
jobs: { exportOrders },
});
/** Supplies the orders service with a validated health result independent of job execution. */
import { defineFunction } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";
export default defineFunction({
id: "orders.health",
input: z.object({}),
output: z.object({ ok: z.literal(true) }),
handler: async () => ({ ok: true as const }),
});
Add src/services/orders/jobs/export-orders.job.ts from the maintained task fixture:
/** Registers the export task as a public job exposing trigger, lookup, list and watch with progress/output fields. */
import { defineJob } from "@relkit/app/jobs";
import exportOrders from "@app/services/orders/tasks/export-orders.task.js";
const exportOrdersJob = defineJob({
id: "orders.export-orders-job",
name: "exportOrders",
task: exportOrders,
client: {
public: true,
operations: ["trigger", "get", "list", "watch"],
fields: ["status", "progress", "output"],
},
});
export default exportOrdersJob;
For this provider, set that job’s client.operations to ["trigger"] before checking.
The task file below supplies the job’s imported target. Create the sample input file when absent. Keep existing durable task/job IDs and bump the task version when changing an already deployed contract.
Read the task before running it
Add or update src/services/orders/tasks/export-orders.task.ts with this task. It
validates order lines and computes CSV text and a total in cents. The durable declaration
does not by itself prove checkpoint or resume support. The sample IDs and SKUs contain no
commas or newlines; escape fields for arbitrary text. This new app keeps task version 1.
The explicit job exposes exportOrders; keep one job with this public name.
import { defineTask } from "@relkit/app/tasks";
import { z } from "@relkit/app/schema";
const order = z.object({
id: z.string().min(1),
sku: z.string().min(1),
quantity: z.number().int().positive(),
unitPriceCents: z.number().int().nonnegative(),
});
const exportOrders = defineTask({
id: "orders.export-orders",
version: "1",
execution: "durable",
input: z.object({ orders: z.array(order) }),
output: z.object({ csv: z.string(), orderCount: z.number().int(), totalCents: z.number().int() }),
handler: async ({ orders }, context) => {
const lines = ["order_id,sku,quantity,total_cents"];
let totalCents = 0;
for (const item of orders) {
const lineTotal = item.quantity * item.unitPriceCents;
totalCents += lineTotal;
lines.push([item.id, item.sku, item.quantity, lineTotal].join(","));
}
context.log.info("Orders CSV prepared", { orderCount: orders.length, totalCents });
return { csv: lines.join("\n") + "\n", orderCount: orders.length, totalCents };
},
});
export default exportOrders;
Create or update examples/export-orders.json with these two rows. A completed task should
return orderCount: 2, totalCents: 2100, and CSV line totals 900 and 1200.
{
"orders": [
{ "id": "order-1", "sku": "tea", "quantity": 2, "unitPriceCents": 450 },
{ "id": "order-2", "sku": "mug", "quantity": 1, "unitPriceCents": 1200 }
]
}
Check the local profile before submitting
The task source can pass RELKIT checks, but these commands are a diagnostic gate. If
local status reports an unhealthy provider, stop here; a RELKIT jobs list result only
proves that the compiler discovered the job, not that Trigger.dev registered it.
bun run check
bun run typecheck
bun run relkit local up --detach
bun run relkit local status
docker ps --filter name=service-trigger --format '{{.Names}} {{.Status}} {{.Ports}}'
If the Trigger container exits, stop here and inspect its logs. If a future compatibility
fix makes the provider and worker ready, start bun run dev, then use jobs list to
confirm RELKIT discovered exportOrders, then submit the input. An accepted receipt is
only an enqueueing signal. Require all three before claiming the integration works:
the task appears in Trigger.dev’s project dashboard, its native run reaches COMPLETED,
and the run output includes orderCount: 2 and totalCents: 2100.
bun run relkit jobs capabilities
bun run relkit --json jobs list
bun run relkit --json jobs trigger --job exportOrders --input-file examples/export-orders.json --operation-id orders-csv-1
bun run relkit jobs runs get --run-id <runId>
Replace <runId> with the accepted handle only if submission succeeds. Compare it with the
native Trigger.dev run ID and worker logs. No screenshot of a completed native run is shown
here because this profile did not reach that state in a fresh app. A screenshot of
jobs list, an accepted receipt, or a healthy Docker container would not prove that the
task was published and executed.
Current verification limit: The local-source task passes check and typecheck, but
the pinned Trigger image requires service environment variables absent from the current
recipe and listens on a different port. Once the image was cached, local up briefly
reported healthy while the Trigger container exited. Adding the SDK manually allowed the
RELKIT backend to start during a diagnostic run, but submission returned an unknown outcome
and the worker never became ready. We therefore have no proof that this task was
registered with Trigger.dev or executed there. This provider’s runtime compatibility remains
unverified in the capability evidence. Do not rely on
checkpoint resume, durable sleep, observation, or cancellation until the selected environment
reports and demonstrates them. Keep worker publication and definition registration staged so
an unsuccessful replacement does not remove the active worker generation. Continue with
provider comparison to choose a profile by verified capability.