Restate.Sdk.Lambda 0.1.0-alpha.2

This is a prerelease version of Restate.Sdk.Lambda.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Restate.Sdk.Lambda --version 0.1.0-alpha.2
                    
NuGet\Install-Package Restate.Sdk.Lambda -Version 0.1.0-alpha.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Restate.Sdk.Lambda" Version="0.1.0-alpha.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Restate.Sdk.Lambda" Version="0.1.0-alpha.2" />
                    
Directory.Packages.props
<PackageReference Include="Restate.Sdk.Lambda" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Restate.Sdk.Lambda --version 0.1.0-alpha.2
                    
#r "nuget: Restate.Sdk.Lambda, 0.1.0-alpha.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Restate.Sdk.Lambda@0.1.0-alpha.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Restate.Sdk.Lambda&version=0.1.0-alpha.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Restate.Sdk.Lambda&version=0.1.0-alpha.2&prerelease
                    
Install as a Cake Tool

CI NuGet Documentation Discord Twitter

Restate .NET SDK

Pre-release (0.1.0-alpha.2) -- Under active development. APIs may change between releases.

This is a community-driven project, not an official Restate SDK. Built by reverse-engineering the Java, TypeScript, and Go SDKs. For official SDKs, see github.com/restatedev.

Restate is a system for easily building resilient applications using distributed durable async/await. This repository contains a .NET SDK for writing services that run on the Restate runtime.

Community

Using the SDK

Prerequisites

  • .NET 10.0 SDK or later

  • Restate Server or Restate CLI

    Alternatively, use the included Docker Compose file:

    docker compose up -d
    

Install

dotnet add package Restate.Sdk --version 0.1.0-alpha.2

Optional packages:

dotnet add package Restate.Sdk.Testing --version 0.1.0-alpha.2   # Mock contexts for unit testing
dotnet add package Restate.Sdk.Lambda --version 0.1.0-alpha.2    # AWS Lambda adapter

The Roslyn source generator is bundled with Restate.Sdk -- typed clients and service definitions are generated automatically at compile time. No additional packages needed.

Quick Start

Define a service and host it:

using Restate.Sdk;
using Restate.Sdk.Hosting;

[Service]
public class GreeterService
{
    [Handler]
    public async Task<string> Greet(Context ctx, string name)
    {
        // Side effect: journaled and replayed on retries
        var greeting = await ctx.Run("build-greeting",
            () => $"Hello, {name}!");

        return greeting;
    }
}

await RestateHost.CreateBuilder()
    .AddService<GreeterService>()
    .Build()
    .RunAsync();

Start the service and register it with Restate:

dotnet run

restate deployments register http://localhost:9080

Invoke the service:

curl -X POST http://localhost:8080/GreeterService/Greet \
  -H 'content-type: application/json' \
  -d '"World"'

Service Types

Restate supports three service types, each with different consistency and state guarantees.

Stateless Service

No state. Multiple invocations run concurrently.

[Service]
public class EmailService
{
    [Handler]
    public async Task<bool> SendEmail(Context ctx, EmailRequest request)
    {
        return await ctx.Run("send-email", async () =>
        {
            await emailClient.SendAsync(request.To, request.Subject, request.Body);
            return true;
        });
    }
}
Virtual Object

Keyed entities with exclusive state access. Only one [Handler] runs at a time per key. [SharedHandler] methods can run concurrently with read-only state access.

[VirtualObject]
public class Counter
{
    private static readonly StateKey<int> Count = new("count");

    [Handler]
    public async Task<int> Add(ObjectContext ctx, int delta)
    {
        var current = await ctx.Get(Count);
        var next = current + delta;
        ctx.Set(Count, next);
        return next;
    }

    [SharedHandler]
    public async Task<int> Get(SharedObjectContext ctx)
        => await ctx.Get(Count);

    [Handler]
    public Task Reset(ObjectContext ctx)
    {
        ctx.ClearAll();
        return Task.CompletedTask;
    }
}
Workflow

Long-running durable workflows with state and awakeables for external signaling. The Run handler executes exactly once per workflow ID. Workflow promises (ctx.Promise<T>()) are also available for signaling between handlers.

[Workflow]
public class SignupWorkflow
{
    private static readonly StateKey<string> Status = new("status");

    [Handler]
    public async Task<bool> Run(WorkflowContext ctx, SignupRequest request)
    {
        ctx.Set(Status, "creating-account");

        var accountId = await ctx.Run("create-account",
            () => AccountService.Create(request.Email, request.Name));

        ctx.Set(Status, "awaiting-verification");

        // Awakeable: a durable promise resolved by an external system.
        // Pass awakeable.Id to the external system; await awakeable.Value to block.
        var awakeable = ctx.Awakeable<string>();

        await ctx.Run("send-verification-email",
            () =>
            {
                EmailService.SendVerification(request.Email, awakeable.Id);
                return Task.CompletedTask;
            });

        // Workflow suspends here until the external system resolves the awakeable
        await awakeable.Value;

        ctx.Set(Status, "completed");
        return true;
    }

    [SharedHandler]
    public async Task<string> GetStatus(SharedWorkflowContext ctx)
        => await ctx.Get(Status) ?? "unknown";
}

Durable Building Blocks

The Context object provides durable operations that are automatically journaled and replayed:

// Side effects (journaled, replayed on retries)
var result = await ctx.Run("name", async () => await FetchDataAsync());
var value = await ctx.Run("name", () => ComputeSync());

// Service-to-service calls (retried, exactly-once)
var response = await ctx.Call<string>("GreeterService", "Greet", "Alice");
var count = await ctx.Call<int>("CounterObject", "my-key", "Add", 1);

// One-way sends (fire-and-forget, returns InvocationHandle for tracking)
InvocationHandle handle = await ctx.Send("EmailService", "SendEmail", request);
await ctx.Send("ReminderService", "Remind", data, delay: TimeSpan.FromHours(1));

// Durable timers (survive restarts)
await ctx.Sleep(TimeSpan.FromMinutes(5));

// Non-blocking timer (returns a future for use with combinators)
var timer = ctx.Timer(TimeSpan.FromMinutes(5));

// Awakeables (promises resolved by external systems)
var awakeable = ctx.Awakeable<string>();
// pass awakeable.Id to external system, then:
var payload = await awakeable.Value;

// Non-blocking futures and combinators
var f1 = ctx.RunAsync<int>("a", () => Task.FromResult(1));
var f2 = ctx.RunAsync<int>("b", () => Task.FromResult(2));
var results = await ctx.All(f1, f2);     // wait for all
var winner = await ctx.Race(f1, f2);     // first to complete

// Replay-safe random
var id = ctx.Random.NextGuid();
var n = ctx.Random.Next(1, 100);

// Replay-safe console (silent during replay)
ctx.Console.Log("processing...");

// Durable timestamp
var now = await ctx.Now();

// Context properties
var invocationId = ctx.InvocationId;    // unique ID for this invocation
var headers = ctx.Headers;              // request headers
CancellationToken ct = ctx.Aborted;     // fires when invocation is cancelled

Error Handling

Restate automatically retries failed handlers. To signal a non-retryable failure (validation errors, business rule violations), throw a TerminalException:

// Non-retryable error -- Restate will NOT retry this invocation
throw new TerminalException("Order not found", 404);

// All other exceptions are retried automatically with exponential backoff

ASP.NET Core Integration

For applications that need full dependency injection:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRestate(opts =>
{
    opts.AddService<GreeterService>();
    opts.AddVirtualObject<CounterObject>();
    opts.AddWorkflow<SignupWorkflow>();
});

builder.Services.AddScoped<IEmailClient, SmtpEmailClient>();

var app = builder.Build();
app.MapRestate();
await app.RunAsync();

AWS Lambda

Deploy handlers as Lambda functions using the Restate.Sdk.Lambda package:

using Restate.Sdk.Lambda;

public class Handler : RestateLambdaHandler
{
    public override void Register(Action<Type> bind)
    {
        bind(typeof(GreeterService));
        bind(typeof(CounterObject));
    }
}

Configure the Lambda function handler as YourAssembly::YourNamespace.Handler::FunctionHandler.

Testing

The Restate.Sdk.Testing package provides mock contexts for unit testing handlers without a running Restate server:

using Restate.Sdk.Testing;

var ctx = new MockContext();
var service = new GreeterService();
var result = await service.Greet(ctx, "Alice");
Assert.Equal("Hello, Alice!", result);

Mock contexts are available for every context type:

Mock Class For
MockContext Stateless services
MockObjectContext Virtual object exclusive handlers
MockSharedObjectContext Virtual object shared handlers
MockWorkflowContext Workflow run handlers
MockSharedWorkflowContext Workflow shared handlers

Mock context features:

// Deterministic time
var ctx = new MockContext();
ctx.CurrentTime = new DateTimeOffset(2024, 6, 15, 12, 0, 0, TimeSpan.Zero);
var now = await ctx.Now(); // returns the configured time

// Setup call results
ctx.SetupCall<string>("GreeterService", "Greet", "Hello!");

// Setup call failures
ctx.SetupCallFailure("GreeterService", "Greet", new TerminalException("fail", 500));

// Register typed clients
ctx.RegisterClient<IGreeterServiceClient>(myMockClient);

// Verify recorded calls, sends, and sleeps
Assert.Single(ctx.Calls);
Assert.Equal("GreeterService", ctx.Calls[0].Service);

Interfaces

Context interfaces (IContext, IObjectContext, etc.) are available for utility methods, type constraints, and generic programming:

// Utility method accepting any context type
public static async Task<string> FormatTimestamp(IContext ctx)
{
    var now = await ctx.Now();
    return now.ToString("O");
}

External Ingress Client

Call Restate services from outside the runtime using RestateClient:

using Restate.Sdk.Client;

using var client = new RestateClient("http://localhost:8080");

// Call a service handler
var greeting = await client.Service("GreeterService").Call<string>("Greet", "World");

// Call a virtual object
var count = await client.VirtualObject("CounterObject", "my-key").Call<int>("Add", 1);

// Start a workflow
await client.Workflow("SignupWorkflow", "user-1").Call<bool>("Run", "alice@example.com");

// Fire-and-forget with delay (returns invocation ID)
var invocationId = await client.Service("EmailService")
    .Send("SendEmail", request, delay: TimeSpan.FromHours(1));

Samples

The samples/ directory contains complete working examples:

Sample Port Demonstrates
Greeter 9080 Service basics, ctx.Run(), ctx.Sleep()
Counter 9081 Virtual object state, StateKey<T>, shared handlers
TicketReservation 9082 State machines, delayed sends, cross-service calls
SignupWorkflow 9084 Workflows, durable promises, awakeables

Run any sample:

cd samples/Greeter
dotnet run

Compatibility

SDK Version Restate Server Protocol .NET
0.1.0-alpha.2 1.6.0+ v5 - v6 .NET 10.0

Contributing

Contributions are welcome. Whether feature requests, bug reports, or pull requests, all contributions are appreciated.

Building from source

dotnet build
dotnet test

Running specific tests

dotnet test test/Restate.Sdk.Tests --filter "FullyQualifiedName~ProtobufParser"
dotnet test test/Restate.Sdk.Generators.Tests

Code formatting

The CI pipeline enforces consistent formatting. Check locally before pushing:

dotnet format --verify-no-changes

CI

Pull requests run two GitHub Actions jobs automatically:

  • Build & Test -- builds in Release mode and runs all tests
  • Format Check -- verifies dotnet format compliance

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-alpha.5 41 2/23/2026
0.1.0-alpha.4 41 2/23/2026
0.1.0-alpha.3 45 2/11/2026
0.1.0-alpha.2 45 2/10/2026
0.1.0-alpha.1 44 2/10/2026