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 };
    },
  },
});

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.service.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";

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({ name: "OrderProcessor" })
      .process({ id: orderId, item: req.item });
    return { orderId };
  },
);
8

You're running Restate handlers inside Encore!