greet: function*(name: string) {
return `Hello, ${name}!`;
},greet: typed(
{ input: serde.proto<GreetReq>(), output: serde.proto<GreetRes>() },
function*({ name }) { return { message: `Hello, ${name}!` }; }
),Equivalent to typed({ input: serde.schema(A), output: serde.schema(B) }, fn).
greet: schemas(
{ input: GreetReqSchema, output: GreetResSchema },
function*({ name }) { return { message: `Hello, ${name}!` }; }
),restate.service / restate.object / restate.workflow return { iface, implementation }.
Options are a top-level options block; handler names in options.handlers are
type-checked against the handlers map.
const { iface: greeterIface, implementation: greeter } = restate.service({
name: "greeter",
handlers: {
*greet(name: string) {
return `Hello, ${name}!`;
},
*greetWithRetention(name: string) {
return `Hello, ${name}!`;
},
greetTyped: typed(
{ input: serde.proto<GreetReq>(), output: serde.proto<GreetRes>() },
function*({ name }) { return { message: `Hello, ${name}!` }; }
),
},
options: {
...serviceLevelOptions,
handlers: {
greetWithRetention: { idempotencyRetention: "5m" },
},
},
});
const { iface: counterIface, implementation: counter } = restate.object({
name: "counter",
handlers: {
*increment(delta: number) {
const cur = yield* restate.state.get("count");
yield* restate.state.set("count", (cur ?? 0) + delta);
},
*count() {
return (yield* restate.state.get("count")) ?? 0;
},
},
options: {
handlers: {
count: { shared: true },
},
},
});
// Calling other services ? use the iface directly as a client handle
const { implementation: caller } = restate.service({
name: "caller",
handlers: {
*run() {
const greeter = client(greeterIface);
const result = yield* greeter.greet("world");
const counter = client(counterIface, "my-counter");
yield* counter.increment(1);
const n = yield* counter.count();
return `${result}, count is ${n}`;
},
},
});When callers live in a separate package, define the interface independently with
restate.interface.* and publish it without the implementation as a dependency.
The interface carries only type information ? no generator functions, no runtime
options.
// @my-org/greeter-iface
export const greeterIface = restate.interface.service("greeter", {
greet: restate.interface.typed<string, string>(),
greetProto: restate.interface.typed({ input: serde.proto<GreetReq>(), output: serde.proto<GreetRes>() }),
greetSchema: restate.interface.schemas({ input: GreetReqSchema, output: GreetResSchema }),
});
export const counterIface = restate.interface.object("counter", {
increment: restate.interface.typed<number, void>(),
count: restate.interface.typed<void, number>(),
});The implementation imports the interface and calls iface.implement(). Same
handlers + options shape as the standalone path.
// @my-org/greeter-impl
import { greeterIface, counterIface } from "@my-org/greeter-iface";
import { implement } from "@restate/sdk";
const greeter = implement(greeterIface, {
handlers: {
*greet(name) { return `Hello, ${name}!`; },
*greetProto({ name }) { return { message: `Hello, ${name}!` }; },
*greetSchema({ name }) { return { message: `Hello, ${name}!` }; },
},
options: {
handlers: {
greet: { retryPolicy: { maxAttempts: 3 } },
},
},
});
const counter = implement(counterIface, {
handlers: {
*increment(delta) {
const cur = yield* restate.state.get("count");
yield* restate.state.set("count", (cur ?? 0) + delta);
},
*count() {
return (yield* restate.state.get("count")) ?? 0;
},
},
options: {
handlers: {
count: { shared: true },
},
},
});Callers depend only on @my-org/greeter-iface and use it as a client handle
directly ? identical to the standalone case.