Overview
The Restate Go SDK is open source and can be found on GitHub: (sdk-go repo).
Have a look at the Go Quickstart!
Add the github.com/restatedev/sdk-go
dependency to your project to start developing Restate services.
The Restate SDK lets you implement handlers. Handlers can either be part of a Service, or a Virtual Object. Let's have a look at how to define them.
Services​
Services and their handlers are defined as follows:
type MyService struct{}func (MyService) MyHandler(ctx restate.Context, greeting string) (string, error) {return fmt.Sprintf("%s!", greeting), nil}func main() {if err := server.NewRestate().Bind(restate.Reflect(MyService{})).Start(context.Background(), "0.0.0.0:9080"); err != nil {log.Fatal(err)}}
- Define a Service by implementing exported handlers on any struct.
- By default, the service name is the name of the struct, so the service can be
called at
<RESTATE_INGRESS_URL>/MyService/MyHandler
. - The service definition contains a list of handlers.
Each handler is just function that implements the handler logic. The handler
will be named the same way as the method.
The function has the Restate Context as its first argument.
Within the handler, you use the
Context
to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable. - The handler input parameters and return type can be of any type, as long as
they can be serialized. By default, serialization is done with
JSONCodec
which usesencoding/json
. Input types, output types, and even errors are not mandatory and can be omitted if your handler doesn't need them. - Pass the
MyService
struct torestate.Reflect
which uses reflection to turn the methods into handlers. It will skip unexported methods and those that don't have the expected signature - see the package documentation for a list of allowed signatures. - Finally, create a server listening on the specified address and bind the service(s) to it.
Virtual Objects​
Virtual Objects and their handlers are defined similarly to services, with the following differences:
type MyVirtualObject struct{}func (MyVirtualObject) MyHandler(ctx restate.ObjectContext, greeting string) (string, error) {return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil}func (MyVirtualObject) MyConcurrentHandler(ctx restate.ObjectSharedContext, greeting string) (string, error) {return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil}func main() {if err := server.NewRestate().Bind(restate.Reflect(MyVirtualObject{})).Start(context.Background(), "0.0.0.0:9080"); err != nil {log.Fatal(err)}}
- Define a Virtual Object by implementing exported handlers on any struct.
- The first argument of each handler must be the
ObjectContext
parameter. Handlers with theObjectContext
parameter can write to the K/V state store. Only one handler can be active at a time, to ensure consistency. - You can retrieve the key of the object you are in via
restate.Key(ctx)
. - If you want to have a handler that executes concurrently to the others and
doesn't have write access to the K/V state, instead use the
ObjectSharedContext
. You can use these handlers, for example, to read K/V state and expose it to the outside world, or to interact with the blocking handler (e.g. resolve awakeables).
The Go SDK also supports defining handlers and input/output types using code generation from Protocol Buffers. See Code Generation for more details.
Now that you have a high-level idea of what a Restate service might look like, let's have a look at what the Restate Context allows you to do.
Workflows​
Workflows are a special type of Virtual Objects, their definition is similar but with the following differences:
type MyWorkflow struct{}func (MyWorkflow) Run(ctx restate.WorkflowContext, req string) (string, error) {// implement the workflow logic herereturn "success", nil}func (MyWorkflow) InteractWithWorkflow(ctx restate.WorkflowSharedContext) error {// implement interaction logic here// e.g. resolve a promise that the workflow is waiting onreturn nil}func main() {server := server.NewRestate().Bind(restate.Reflect(MyWorkflow{}))if err := server.Start(context.Background(), ":9080"); err != nil {slog.Error("application exited unexpectedly", "err", err.Error())os.Exit(1)}}
- Define a Workflow by implementing exported handlers on any struct.
- Every workflow implementation needs to have a handler called
Run
that implements the workflow logic. This handler uses theWorkflowContext
to interact with the SDK. - The
Run
handler executes exactly one time per workflow execution/object. You can retrieve the ID of the workflow execution viarestate.Key(ctx)
. - The
Run
handler executes a set of steps/activities. These can be inlined SDK actions (for example run block or sleep), or abstracted into calls to other handlers. - The other handlers of the workflow are used to interact with the workflow: either query it, or signal it.
They use the
WorkflowSharedContext
to interact with the SDK. These handlers can run concurrently with theRun
handler and can still be called after theRun
handler has finished. - Have a look at the workflow docs to learn more.
Now that you have a high-level idea of what a Restate service might look like, let's have a look at what the Restate Context allows you to do.