The Restate Java/Kotlin 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 Java/Kotlin SDK.

Prerequisites

Contact us on Discord/Slack if you need earlier Java versions.

Getting started

Get started quickly with the Java or Kotlin Quickstart.
Use the following dependencies:
// Annotation processor
annotationProcessor("dev.restate:sdk-api-gen:2.4.0")

// For HTTP services
implementation("dev.restate:sdk-java-http:2.4.0")
// Or for Lambda services
implementation("dev.restate:sdk-java-lambda:2.4.0")

Basic Services

Basic Services group related handlers and expose them as callable endpoints:
@Service
public class MyService {
  @Handler
  public String myHandler(Context ctx, String greeting) {
    return greeting + "!";
  }

  public static void main(String[] args) {
    RestateHttpServer.listen(Endpoint.bind(new MyService()));
  }
}
  • Define a service using the @Service and @Handler annotations
  • Each handler can be called at <RESTATE_INGRESS>/MyService/myHandler. To override the service name (default is simple class name), use the annotation @Name.
  • Handlers take the Context (JavaDocs/KotlinDocs) as the first argument.
  • The input parameter (at most one) and return type are optional and can be of any type. See serialization for more details.
  • Create an endpoint to expose the service over HTTP (port 9080 by default).

Virtual Objects

Virtual Objects are services that are stateful and key-addressable — each object instance has a unique ID and persistent state.
@VirtualObject
public class MyObject {

  @Handler
  public String myHandler(ObjectContext ctx, String greeting) {
    String objectId = ctx.key();

    return greeting + " " + objectId + "!";
  }

  @Shared
  public String myConcurrentHandler(SharedObjectContext ctx, String input) {
    return "my-output";
  }

  public static void main(String[] args) {
    RestateHttpServer.listen(Endpoint.bind(new MyObject()));
  }
}
  • Use the @VirtualObject annotation.
  • The first argument of the handler must be the ObjectContext parameter (JavaDocs/KotlinDocs).
  • 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 use the @Shared annotation and the SharedObjectContext (JavaDocs/KotlinDocs).

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.
@Workflow
public class MyWorkflow {

  @Workflow
  public String run(WorkflowContext ctx, String input) {

    // implement workflow logic here

    return "success";
  }

  @Shared
  public String interactWithWorkflow(SharedWorkflowContext ctx, String input) {
    // implement interaction logic here
    return "my result";
  }

  public static void main(String[] args) {
    RestateHttpServer.listen(Endpoint.bind(new MyWorkflow()));
  }
}
  • Create the workflow by using the @Workflow annotation.
  • Every workflow must include a run handler:
    • This is the main orchestration entry point
    • It runs exactly once per workflow execution and uses the WorkflowContext (JavaDocs/KotlinDocs)
    • 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 must use the SharedWorkflowContext (JavaDocs/KotlinDocs) 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.