Console to use for logging. It attaches to each log message some contextual information, such as invoked service method and invocation id, and automatically excludes logs during replay.
Deterministic date.
Deterministic random methods; these are inherently predictable (seeded on the invocation ID, which is not secret) and so should not be used for any cryptographic purposes. They are useful for identifiers, idempotency keys, and for uniform sampling from a set of options. If a cryptographically secure value is needed, please generate that externally and capture the result with a side effect.
Calls to these methods from inside ctx.run
are disallowed and will fail - side effects must be idempotent, and
these calls are not.
Attach to an invocation
the invocation id to attach to
Optional
serde: Serde<T>the serde to use for the result, default to JSON serde.
Register an awakeable and pause the processing until the awakeable ID (and optional payload) have been returned to the service
(via ctx.completeAwakeable(...)). The SDK deserializes the payload with JSON.parse(result.toString()) as T
.
Retryable errors and terminal errors
const awakeable = ctx.awakeable<string>();
// send the awakeable ID to some external service that will wake this one back up
// The ID can be retrieved by:
const id = awakeable.id;
// ... send to external service ...
// Wait for the external service to wake this service back up
const result = await awakeable.promise;
Clear/delete state in the Restate runtime.
key of the state to delete
Get/retrieve state from the Restate runtime.
Note that state objects are serialized with Buffer.from(JSON.stringify(theObject))
and deserialized with JSON.parse(value.toString()) as T
.
key of the state to retrieve
Optional
serde: Serde<TState extends UntypedState ? TValue : TState[TKey]>a Promise that is resolved with the value of the state key
Same as serviceClient but for virtual objects.
the virtual object key
Same as serviceSendClient but for virtual objects.
the virtual object key
Optional
opts: SendOptionsSend options
Reject an awakeable. When rejecting, the service waiting on this awakeable will be woken up with a terminal error with the provided reason.
the string ID of the awakeable. This is supplied by the service that needs to be woken up.
the reason of the rejection.
Returns the raw request that triggered that handler. Use that object to inspect the original request headers
Resolve an awakeable.
the string ID of the awakeable. This is supplied by the service that needs to be woken up.
Optional
payload: Tthe payload to pass to the service that is woken up.
The SDK serializes the payload with Buffer.from(JSON.stringify(payload))
and deserializes it in the receiving service with JSON.parse(result.toString()) as T
.
Optional
serde: Serde<T>Run an operation and store the result in Restate. The operation will thus not be re-run during a later replay, but take the durable result from Restate.
This let you capture potentially non-deterministic computation and interaction with external systems in a safe way.
Failure semantics are:
You can customize retry options by either:
retryAfter
option. This can be especially useful when interacting with HTTP requests returning the Retry-After
header. You can combine the usage of throwing RetryableError with the maxRetryAttempts
/maxRetryDuration
from RunOptions.const result = await ctx.run(someExternalAction)
const result = await ctx.run("my action", someExternalAction, { maxRetryAttempts: 10 })
await ctx.run("payment action", async () => {
const result = await paymentProvider.charge(txId, paymentInfo);
if (result.paymentRejected) {
// this action will not be retried anymore
throw new TerminalError("Payment failed");
} else if (result.paymentGatewayBusy) {
// restate will retry automatically
// to bound retries, use RunOptions
throw new Error("Payment gateway busy");
} else {
// success!
}
});
await ctx.run("payment action", async () => {
const res = fetch(...);
if (!res.ok) {
// Read Retry-After header
const retryAfterHeader = res.headers['Retry-After']
// Use RetryableError to customize in how long to retry
throw RetryableError.from(cause, { retryAfter: { seconds: retryAfterHeader } })
}
}, {
// Retry at most ten times
maxRetryAttempts: 10
});
Same as run, but providing a name, used for observability purposes.
See run
Makes a type-safe request/response RPC to the specified target service.
The RPC goes through Restate and is guaranteed to be reliably delivered. The RPC is also journaled for durable execution and will thus not be duplicated when the handler is re-invoked for retries or after suspending.
This call will return the result produced by the target handler, or the Error, if the target handler finishes with a Terminal Error.
This call is a suspension point: The handler might suspend while awaiting the response and resume once the response is available.
Service Side:
const service = restate.service(
name: "myservice",
handlers: {
someAction: async(ctx: restate.Context, req: string) => { ... },
anotherAction: async(ctx: restate.Context, count: number) => { ... }
});
// option 1: export only the type signature
export type Service = typeof service;
restate.endpoint().bind(service).listen(9080);
Client side:
// option 1: use only types and supply service name separately
const result1 = await ctx.serviceClient<Service>({name: "myservice"}).someAction("hello!");
// option 2: use full API spec
type MyService: Service = { name: "myservice" };
const result2 = await ctx.serviceClient(Service).anotherAction(1337);
Makes a type-safe one-way RPC to the specified target service. This method effectively behaves like enqueuing the message in a message queue.
The message goes through Restate and is guaranteed to be reliably delivered. The RPC is also journaled for durable execution and will thus not be duplicated when the handler is re-invoked for retries or after suspending.
This call will return immediately; the message sending happens asynchronously in the background. Despite that, the message is guaranteed to be sent, because the completion of the invocation that triggers the send (calls this function) happens logically after the sending. That means that any failure where the message does not reach Restate also cannot complete this invocation, and will hence recover this handler and (through the durable execution) recover the message to be sent.
Optional
opts: SendOptionsService Side:
const service = restate.service(
name: "myservice",
handlers: {
someAction: async(ctx: restate.Context, req: string) => { ... },
anotherAction: async(ctx: restate.Context, count: number) => { ... }
});
// option 1: export only the type signature of the router
export type MyApi = typeof service;
// option 2: export the API definition with type and name (name)
const MyService: MyApi = { name: "myservice" };
restate.endpoint().bind(service).listen(9080);
Client side:
// option 1: use only types and supply service name separately
ctx.serviceSendClient<MyApi>({name: "myservice"}).someAction("hello!");
// option 2: use full API spec
ctx.serviceSendClient(MyService).anotherAction(1337);
Set/store state in the Restate runtime.
Note that state objects are serialized with Buffer.from(JSON.stringify(theObject))
and deserialized with JSON.parse(value.toString()) as T
.
key of the state to set
value to set
Optional
serde: Serde<TState extends UntypedState ? TValue : TState[TKey]>Sleep until a timeout has passed.
either Duration type or milliseconds.
Optional
name: stringObservability name. This will be shown in the UI. This is a lower-bound.
Same as serviceClient but for workflows.
the workflow key
Same as objectSendClient but for workflows.
the workflow key
The context that gives access to all Restate-backed operations, for example
This context can be used only within virtual objects.