Skip to main content
Encore is an open-source backend framework for TypeScript (and Go) with type-safe APIs, declarative infrastructure, and built-in observability. You can run Restate service handlers inside an Encore app by exposing them via raw endpoints, which give you direct access to the Node.js request/response objects. This lets you use Encore for your API layer, databases, and infrastructure while Restate handles durable execution.
1

Create an Encore app

Follow the Encore quickstart to install the CLI, then create a new app:
encore app create
2

Install the Restate SDK

Add the Restate TypeScript SDK to your Encore project:
npm install @restatedev/restate-sdk
3

Define your Restate service handlers

Create a Restate service with your handler logic. This is standard Restate code:
order-processor/restate.ts
import * as restate from "@restatedev/restate-sdk";

export const orderProcessor = restate.service({
    name: "OrderProcessor",
    handlers: {
        process: async (ctx: restate.Context, order: { id: string; item: string }) => {
            const confirmation = await ctx.run(() => chargePayment(order));
            await ctx.run(() => reserveInventory(order));
            await ctx.run(() => sendConfirmation(order, confirmation));
            return { status: "completed", orderId: order.id };
        },
    },
});

export type OrderProcessor = typeof orderProcessor;

async function chargePayment(order: { id: string; item: string }) {
    console.log(`Charging payment for order ${order.id}`);
    return `conf-${order.id}`;
}

async function reserveInventory(order: { id: string; item: string }) {
    console.log(`Reserving inventory for ${order.item}`);
}

async function sendConfirmation(order: { id: string; item: string }, confirmation: string) {
    console.log(`Sending confirmation ${confirmation} for order ${order.id}`);
}
Each ctx.run() call is journaled by Restate. If the process crashes mid-execution, Restate replays completed steps and resumes where it left off.
4

Expose the handler via an Encore raw endpoint

Create the Encore service and wire the Restate endpoint handler to a raw endpoint.Since Encore’s raw endpoints use Node.js HTTP types while the Restate SDK provides a Fetch API handler, we need a small adapter to bridge between the two:
order-processor/encore.restate.ts
import { Service } from "encore.dev/service";
export default new Service("order-processor");
order-processor/api.ts
import { api } from "encore.dev/api";
import { createEndpointHandler } from "@restatedev/restate-sdk/fetch";
import { orderProcessor } from "./restate";

const handler = createEndpointHandler({
    services: [orderProcessor],
});

// Catch-all raw endpoint that Restate Server calls into
export const restateEndpoint = api.raw(
    { expose: true, path: "/restate/!rest", method: "*" },
    async (req, resp) => {
        const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
        const body =
            req.method !== "GET" && req.method !== "HEAD"
                ? await new Promise<Buffer>((resolve, reject) => {
                    const chunks: Buffer[] = [];
                    req.on("data", (chunk: Buffer) => chunks.push(chunk));
                    req.on("end", () => resolve(Buffer.concat(chunks)));
                    req.on("error", reject);
                })
                : undefined;

        const webReq = new Request(url.toString(), {
            method: req.method,
            headers: req.headers as Record<string, string>,
            body,
        });

        const webResp = await handler(webReq);

        resp.writeHead(
            webResp.status,
            Object.fromEntries(webResp.headers.entries()),
        );
        const buf = await webResp.arrayBuffer();
        resp.end(Buffer.from(buf));
    },
);
Encore now serves the Restate handler at /restate/*. The Restate Server will call this endpoint to discover and invoke your handlers.
5

Start Restate Server and Encore

In one terminal, start the Restate Server:
restate-server
In another terminal, start your Encore app:
encore run
6

Register your service with Restate

Tell the Restate Server where to find your handlers:
restate deployments register --use-http1.1 http://localhost:4000/restate
You should see the discovered OrderProcessor service printed out.
The --use-http1.1 flag is needed because Encore’s raw endpoints serve HTTP/1.1.
7

Invoke your handler

Send a request to Restate Server’s ingress (port 8080), which routes it to your Encore-hosted handler:
curl localhost:8080/OrderProcessor/process \
  --json '{"id": "order-123", "item": "widget"}'
You can also invoke the handler from other Encore API endpoints using the Restate client:
order-processor/orders.ts
import { api } from "encore.dev/api";
import * as restate from "@restatedev/restate-sdk-clients";
import { OrderProcessor } from "./restate";

const restateClient = restate.connect({ url: "http://localhost:8080" });

export const createOrder = api(
    { method: "POST", path: "/orders", expose: true },
    async (req: { item: string }): Promise<{ orderId: string }> => {
        const orderId = `order-${Date.now()}`;
        await restateClient
            .serviceSendClient<OrderProcessor>({ name: "OrderProcessor" })
            .process({ id: orderId, item: req.item });
        return { orderId };
    },
);
8

You're running Restate handlers inside Encore!