Restate Typescript SDK
    Preparing search index...

    Interface ContextInternalExperimental

    Internal Context interface exposing additional features.

    Please note that this API is to be considered experimental and might change without notice.

    interface ContextInternal {
        console: Console;
        date: ContextDate;
        rand: Rand;
        attach<T>(invocationId: InvocationId, serde?: Serde<T>): RestatePromise<T>;
        awakeable<T>(serde?: Serde<T>): { id: string; promise: RestatePromise<T> };
        cancel(invocationId: InvocationId): void;
        cancellation(): RestatePromise<void>;
        cancelPreviousCalls(): RestatePromise<InvocationId[]>;
        genericCall<
            REQ = Uint8Array<ArrayBufferLike>,
            RES = Uint8Array<ArrayBufferLike>,
        >(
            call: GenericCall<REQ, RES>,
        ): InvocationPromise<RES>;
        genericSend<REQ = Uint8Array<ArrayBufferLike>>(
            call: GenericSend<REQ>,
        ): InvocationHandle;
        invocation(invocationId: InvocationId): InvocationReference;
        isProcessing(): boolean;
        objectClient<D>(
            opts: VirtualObjectDefinitionFrom<D>,
            key: string,
        ): Client<VirtualObject<D>>;
        objectSendClient<D>(
            obj: VirtualObjectDefinitionFrom<D>,
            key: string,
        ): SendClient<VirtualObject<D>>;
        rejectAwakeable(id: string, reason: string | TerminalError): void;
        request(): Request;
        resolveAwakeable<T>(id: string, payload?: T, serde?: Serde<T>): void;
        run<T>(action: RunAction<T>): RestatePromise<T>;
        run<T>(name: string, action: RunAction<T>): RestatePromise<T>;
        run<T>(
            name: string,
            action: RunAction<T>,
            options: RunOptions<T>,
        ): RestatePromise<T>;
        serviceClient<D>(opts: ServiceDefinitionFrom<D>): Client<Service<D>>;
        serviceSendClient<D>(
            service: ServiceDefinitionFrom<D>,
        ): SendClient<Service<D>>;
        signal<T>(name: string, serde?: Serde<T>): RestatePromise<T>;
        sleep(duration: number | Duration, name?: string): RestatePromise<void>;
        workflowClient<D>(
            opts: WorkflowDefinitionFrom<D>,
            key: string,
        ): Client<Workflow<D>>;
        workflowSendClient<D>(
            opts: WorkflowDefinitionFrom<D>,
            key: string,
        ): SendClient<Workflow<D>>;
    }

    Hierarchy (View Summary)

    Index

    Properties

    console: Console

    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.

    rand: Rand

    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.

    Methods

    • 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.

      Type Parameters

      • T

      Parameters

      Returns { id: string; promise: RestatePromise<T> }

      • id: the string ID that has to be used to complete the awakaeble by some external service
      • promise: the Promise that needs to be awaited and that is resolved with the payload that was supplied by the service which completed the awakeable
      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;
    • Experimental

      Returns a RestatePromise that resolves with undefined when Restate signals cancellation of the current invocation.

      This method MUST only be used when the handler (or its parent service/endpoint) is configured with explicitCancellation: true. Without configuring this option, cancellations are propagated automatically, and this promise will NEVER resolve.

      Promise reuse: calling this method multiple times returns the same promise instance as long as the current cancellation signal has not yet arrived. Once the promise resolves (cancellation received), later calls return a new promise that will resolve on the next cancellation signal.

      Returns RestatePromise<void>

      const greeter = restate.service({
      name: "greeter",
      handlers: {
      greet: async (ctx: restate.Context, name: string) => {
      ctxInternal = ctx as restate.internal.ContextInternal;
      const result = await RestatePromise.race([
      ctx.run(() => longRunningTask(name)),
      ctxInternal.cancellation().map(() => { throw new restate.TerminalError("Cancelled") }),
      ]);
      return result;
      },
      },
      options: { explicitCancellation: true },
      });
      const greeter = restate.service({
      name: "greeter",
      handlers: {
      greet: async (ctx: restate.Context, name: string) => {
      const ctxInternal = ctx as restate.internal.ContextInternal;
      const controller = new AbortController();
      const cancellation = ctxInternal.cancellation()
      .map(() => {
      controller.abort();
      throw new restate.TerminalError("Cancelled");
      });

      return RestatePromise.race([
      ctx.run(() => fetch(`https://api.example.com/greet/${name}`, { signal: controller.signal })),
      cancellation,
      ]);
      },
      },
      options: { explicitCancellation: true },
      });
      const greeter = restate.service({
      name: "greeter",
      handlers: {
      greet: async (ctx: restate.Context, name: string) => {
      const ctxInternal = ctx as restate.internal.ContextInternal;
      try {
      return await RestatePromise.race([
      ctx.run(() => longRunningTask(name)),
      ctxInternal.cancellation().map(() => { throw new restate.TerminalError("Cancelled") }),
      ]);
      } catch (e) {
      // Perform cleanup
      await ctx.run(() => cleanupResources(name));

      // After cancellation is resolved, ctx.cancellation() returns a fresh promise.
      // Race cleanup confirmation against the next cancellation signal.
      await RestatePromise.race([
      ctx.run(() => confirmCleanup(name)),
      ctxInternal.cancellation().map(() => { throw new restate.TerminalError("Canceled during cleanup") }),
      ]);
      }
      },
      },
      options: { explicitCancellation: true },
      });
    • Experimental

      Returns true if the handler is in the processing phase. This is the mechanism used by ctx.console to distinguish whether we should log or not in replaying/processing.

      WARNING: This method should not be used to influence control flow, as it will surely lead to non-determinism errors!

      Returns boolean

    • Reject an awakeable. When rejecting, the service waiting on this awakeable will be woken up with a terminal error with the provided reason.

      Parameters

      • id: string

        the string ID of the awakeable. This is supplied by the service that needs to be woken up.

      • reason: string | TerminalError

        the reason of the rejection, either a string message or a TerminalError.

      Returns void

      // The sleeping service should have sent the awakeableIdentifier string to this service.
      ctx.rejectAwakeable(awakeableIdentifier, "super bad error");
    • Resolve an awakeable.

      Type Parameters

      • T

      Parameters

      • id: string

        the string ID of the awakeable. This is supplied by the service that needs to be woken up.

      • Optionalpayload: T

        the 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.

      • Optionalserde: Serde<T>

      Returns void

      Retryable error with custom retry delay
      // The sleeping service should have sent the awakeableIdentifier string to this service.
      ctx.resolveAwakeable(awakeableIdentifier, "hello");
    • 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:

      • If an operation has run and persisted before, the result (value or Error) will be taken from the Restate journal.
      • There is a small window where an action may be re-run, if a failure occurred between a successful run and persisting the result.
      • No second action will be run while a previous run's result is not yet durable. That way, effects that build on top of each other can assume deterministic results from previous runs, and at most one run will be re-executed on replay (the latest, if the failure happened in the small windows described above).

      You can customize retry options by either:

      • Providing retry policy options in RunOptions
      • Throwing RetryableError, providing 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.

      Type Parameters

      • T

      Parameters

      Returns RestatePromise<T>

      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.

      Type Parameters

      • T

      Parameters

      Returns RestatePromise<T>

    • See run

      Type Parameters

      • T

      Parameters

      Returns RestatePromise<T>

    • 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.

      Type Parameters

      • D

      Parameters

      Returns Client<Service<D>>

      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.serve({ services: [service], port: 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.

      Type Parameters

      • D

      Parameters

      Returns SendClient<Service<D>>

      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 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.serve({ services: [service], port: 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);
    • Experimental

      Wait for a named signal to arrive on the current invocation.

      Signals are identified by name and are scoped to the current invocation. Another handler can send a signal to this invocation using InvocationReference.signal, specifying this invocation's ID (available via ctx.request().id) and the same signal name.

      Type Parameters

      • T

      Parameters

      • name: string

        the name of the signal to wait for.

      • Optionalserde: Serde<T>

        optional custom serializer/deserializer for the payload.

      Returns RestatePromise<T>

      a RestatePromise that resolves when the signal arrives.

      const ctxInternal = ctx as restate.internal.ContextInternal;
      const approved = await ctxInternal.signal<boolean>("approved");

      @experimental