Skip to main content
@durable-effect/task is the lower-level package in the durable-effect ecosystem. It gives you direct access to the core primitives — task definitions, state management, scheduling, and the Cloudflare Durable Object integration — without the higher-level abstractions in @durable-effect/jobs.

When to use @durable-effect/task

Use @durable-effect/task when you need more control than @durable-effect/jobs provides:
  • You want to bring your own Durable Object class and wiring
  • You need to colocate multiple task types inside a single DO
  • You’re building a custom runtime or test harness
  • You need low-level access to storage or alarm primitives
If you want a simpler, batteries-included experience with pre-built job queues and scheduling patterns, prefer @durable-effect/jobs instead.

Core concepts

Task.define(config) — defines a state machine. You provide schemas for state and events, then write onEvent and onAlarm handlers. The result is a TaskDefinition value that can be registered, tested, or wired into a Durable Object. TaskContext<S> — the interface every handler receives. It provides typed access to state (recall, save, update), scheduling (scheduleIn, scheduleAt, cancelSchedule, nextAlarm), and lifecycle control (purge). createTasks(definitions) — takes a record of task definitions and produces a ready-to-export Durable Object class (TasksDO) and a typed client accessor (tasks). This is the main entry point for Cloudflare deployments.

Package exports

The package has two entry points:
Import pathPurpose
@durable-effect/taskCore definitions, schemas, testing utilities, service interfaces
@durable-effect/task/cloudflareCloudflare-specific integration: createTasks, makeTaskEngine, storage and alarm adapters

Install

npm install @durable-effect/task effect
@cloudflare/workers-types is an optional peer dependency — add it if you want full type checking for Cloudflare APIs.

Quick start

1. Define a task

// src/tasks/counter.ts
import { Effect, Schema } from "effect"
import { Task } from "@durable-effect/task"
import { createTasks } from "@durable-effect/task/cloudflare"

const CounterState = Schema.Struct({
  count: Schema.Number,
})

const StartEvent = Schema.Struct({
  _tag: Schema.Literal("Start"),
})

const counter = Task.define({
  state: CounterState,
  event: StartEvent,

  onEvent: (ctx, _event) =>
    Effect.gen(function* () {
      yield* ctx.save({ count: 0 })
      yield* ctx.scheduleIn("2 seconds")
    }),

  onAlarm: (ctx) =>
    Effect.gen(function* () {
      const current = yield* ctx.recall()
      const count = (current?.count ?? 0) + 1
      yield* ctx.save({ count })

      if (count >= 10) {
        yield* ctx.purge()
      }

      yield* ctx.scheduleIn("2 seconds")
    }),
})

export const { TasksDO, tasks } = createTasks({ counter })

2. Export the Durable Object

// src/index.ts
export { TasksDO } from "./tasks/counter.js"

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    // your fetch handler
  },
} satisfies ExportedHandler<Env>

3. Configure wrangler

{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [
      {
        "name": "TASKS_DO",
        "class_name": "TasksDO"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_classes": ["TasksDO"]
    }
  ]
}

4. Send events and read state

import { Effect } from "effect"
import { tasks } from "./tasks/counter.js"
import { env } from "cloudflare:workers"

const counter = tasks(env.TASKS_DO, "counter")

// Send an event — payload is type-checked against the task's event schema
yield* counter.send("my-counter-id", { _tag: "Start" }).pipe(Effect.orDie)

// Read the current state — returns CounterState | null
const state = yield* counter.getState("my-counter-id").pipe(Effect.orDie)

Defining tasks

Task.define() config, TaskContext API, service dependencies, and multiple tasks per DO

Testing

In-memory storage and alarm, test stack setup, and complete test examples

Build docs developers (and LLMs) love