Skip to content

Built-in transforms

betterResultCodec serializes Better Result values on the callee and rehydrates them on the caller:

applyTransforms(MyDO, {
all: [registerTransform(betterResultCodec)],
});
const result = await env.MY_DO.get(id)
.with(betterResultCodec)
.findTodo(todoId);

It preserves or rehydrates Result<T, E> return types.

timeout stops the caller from waiting and rejects with CallerTimeoutError:

const report = await env.MY_DO.get(id)
.with(timeout, 5_000)
.loadReport();

errorBoundary converts returned values and thrown errors into Better Results:

const result = await env.MY_DO.get(id)
.with(errorBoundary)
.loadReport();
// Result<Report, unknown>

retry retries caller failures and can delay between attempts:

const result = await env.MY_DO.get(id).with(retry, {
retries: 3,
delay: ({ error, attempt }) => {
console.warn('retrying RPC', { error, attempt });
return attempt * 100;
},
}).loadReport();

attempt is one-based. The delay function can return milliseconds synchronously or through a promise; 0 retries immediately.

largeObjectStream JSON-encodes large objects into a marked ReadableStream, sends 64 KiB chunks, and reconstructs the value on the caller. The default threshold is 32 MiB.

applyTransforms(MyDO, {
methods: {
exportData: [registerTransform(largeObjectStream)],
},
});
const data = await env.MY_DO.get(id)
.with(largeObjectStream)
.exportData();

Configure the threshold on the callee registration when needed:

registerTransform(largeObjectStream, {
thresholdBytes: 10 * 1024 * 1024,
});

Values must be JSON-compatible. Unmarked ReadableStream results pass through unchanged.

abortAsSuccess recognizes the durableObjectReset error caused by this.ctx.abort() and returns Result.ok(undefined). Other errors continue to throw.

const result = await env.MY_DO.get(id)
.with(abortAsSuccess)
.destroy();

Its transformed type is Result<T | undefined, never>.