The Restate Python SDK is open source and available on GitHub. The Restate SDK lets you implement handlers. Handlers can be part of a Basic Service, a Virtual Object, or a Workflow. This page shows how to define them with the Python SDK.

Prerequisites

  • Python >= v3.11

Getting started

Get started quickly with the Python Quickstart.
Add the restate_sdk[serde] dependency to your Python project to start developing Restate services.

Basic Services

Basic Services group related handlers and expose them as callable endpoints:
import restate

my_service = restate.Service("MyService")


@my_service.handler("myHandler")
async def my_handler(ctx: restate.Context, greeting: str) -> str:
    return f"${greeting}!"


app = restate.app([my_service])
  • Initialize the Service and specify the service name (here MyService).
  • Annotate each handler with @my_service.handler(). Optionally, you can override the handler name (here myHandler).
  • Each handler can then be called at <RESTATE_INGRESS>/myService/myHandler
  • Handlers take the restate.Context as the first argument.
  • Handlers can take one optional JSON-serializable input and must return an optional output. These can be of any primitive type, TypedDict or Pydantic model (or see custom serialization for advanced types).
  • Finally, initialize the app, bind the service(s) to it, and serve it. Look at the serving docs to learn more about serving your app.

Virtual Objects

Virtual Objects are services that are stateful and key-addressable — each object instance has a unique ID and persistent state.
import restate

my_object = restate.VirtualObject("MyVirtualObject")


@my_object.handler("myHandler")
async def my_handler(ctx: restate.ObjectContext, greeting: str) -> str:
    return f"${greeting} ${ctx.key()}!"


@my_object.handler(kind="shared")
async def my_concurrent_handler(ctx: restate.ObjectSharedContext, greeting: str) -> str:
    return f"${greeting} ${ctx.key()}!"


app = restate.app([my_object])
  • Initialize a restate.VirtualObject and specify the object’s name (here MyVirtualObject).
  • Each instance is identified by a key (accessible via ctx.key()).
  • Virtual Objects can have exclusive and shared handlers.
  • Exclusive handlers receive an ObjectContext, allowing read/write access to object state.
  • Shared handlers have the annotation kind="shared" and receive an ObjectSharedContext.

Workflows

Workflows are long-lived processes with a defined lifecycle. They run once per key and are ideal for orchestrating multi-step operations, which require external interaction via signals and queries.
import restate

my_workflow = restate.Workflow("MyWorkflow")


@my_workflow.main()
async def run(ctx: restate.WorkflowContext, req: str) -> str:
    # ... implement workflow logic here ---
    return "success"


@my_workflow.handler()
async def interact_with_workflow(ctx: restate.WorkflowSharedContext, req: str):
    # ... implement interaction logic here ...
    return


app = restate.app([my_workflow])
  • Initialize a restate.Workflow and specify its name (here MyWorkflow).
  • Every workflow must include a run handler:
    • This is the main orchestration entry point and is annotated with @my_workflow.main().
    • It runs exactly once per workflow execution and uses the WorkflowContext
    • Resubmission of the same workflow will fail with “Previously accepted”. The invocation ID can be found in the request header x-restate-id.
    • Use ctx.key() to access the workflow’s unique ID
  • Additional handlers are annotated with @my_workflow.handler(). They must use the WorkflowSharedContext and can signal or query the workflow. They can run concurrently with the run handler and until the retention time expires.

Configuring services

Check out the service configuration docs to learn how to configure service behavior, including timeouts and retention policies.