Crux
API Reference@use-crux/core

defer()

Request-scoped deferred work API, inline callbacks and named Runtime targets.

import {
  defer,
  CruxDeferError,
  DEFER_ERROR_CODES,
  type DeferredWorkRef,
  type DeferredCallback,
} from '@use-crux/core'

Overview

defer is a dual overload on the package root. Prefer overloads over casts: named targets come first; unbranded callbacks select the inline form.

defer(callback)

ParameterTypeDescription
callback() => void | PromiseLike<void>Lazy zero-argument callback

Returns: void

Registers work on the nearest compatible invocation scope. The callback starts after the host’s declared completion class. Failures are contained and observed; they do not fail an already-committed response.

Throws:

  • DEFER_SCOPE_REQUIRED: no active scope
  • DEFER_CAPABILITY_MISSING: scope lacks inline support
  • DEFER_SCOPE_SEALED / DEFER_LIMIT_EXCEEDED: lifecycle bounds
  • DEFER_REPLAY_UNSAFE: inside replayable flow execution

defer(target, input)

ParameterTypeDescription
targetbranded RuntimeTaskTargetExported durableTask(...) (or compatible) target
inputinferred JSON-safe inputRequired for V1 targets constrained to JsonValue

Returns: Promise<DeferredWorkRef>

interface DeferredWorkRef {
  readonly kind: 'deferred.work'
  readonly workId: string
  readonly targetId: string
}

Resolves after durable staging acceptance. Execution still waits for successful logical finalization and outbox release. Host adapters must await the scope committed barrier before committing the response.

Throws or poisons commit with:

  • RUNTIME_REQUIRED (cause) / DEFER_COMMIT_FAILED (host barrier)
  • DEFER_CAPABILITY_MISSING when the host cannot finalize named work
  • DEFER_TARGET_INPUT_REQUIRED for missing input at runtime
  • DEFER_REPLAY_UNSAFE inside replayable flows

Host subpaths

ImportPurpose
@use-crux/core/defer/nodeNode HTTP withNodeDefer / createNodeDeferHost
@use-crux/core/defer/serverlesswithWaitUntilDefer, withAfterDefer, withNamedOnlyDefer, factories
@use-crux/nextNext.js withNextDefer binding after()

Internal first-party SPI (not application API):

  • @use-crux/core/internal/defer-host
  • @use-crux/core/internal/defer-lifecycle

Host lifetime semantics

Completion classes

Each host lifetime declares a completion class describing when inline deferred callbacks may start relative to the response:

  • response-finished: callbacks start only after the response has fully finished (Node HTTP via withNodeDefer, Next.js via after(), per-process multiprocess Node hosts).
  • handler-returned: callbacks may start as soon as the handler returns, and may overlap a still-streaming response body (waitUntil-based hosts such as Vercel Functions and Cloudflare Workers).
  • Named-only lifetimes (Lambda withNamedOnlyDefer, Convex bridge runs) have no inline completion class; inline defer(callback) throws DEFER_CAPABILITY_MISSING.

durableFinalization advertisement

A host lifetime advertises durableFinalization: true only when it can await the named-defer commit barrier before committing the response. Named await defer(target, input) requires a configured Runtime plus a lifetime advertising durable finalization; raw Node hosts advertise durableFinalization: false unless the wrapper opts in (for example withNamedOnlyDefer(handler, { durableFinalization: true })).

Capability is explicit, never inferred

Never infer host correctness from environment variable names (VERCEL, etc.). Capability comes only from the wrapper or platform hook that is explicitly installed (waitUntil, after, a lifetime port); an installed package or a recognizable environment alone is not treated as proof of a working lifetime.

Diagnostics

Observed drain outcomes (not always thrown to the handler):

  • DEFER_CALLBACK_FAILED
  • DEFER_TIMED_OUT
  • DEFER_CANCELLED

On this page