1---
2title: Schemaless RPC
3description: Typed RPC from a Lambda Function into a MicroVM with no schema — declare the image's Shape, connect, get the client.
4sidebar:
5 order: 2
6---
7
8The pattern — what a Shape member may be, how the typed client arises, what
9crosses the wire — lives at [Schemaless RPC](/apis/schemaless). This page
10covers the AWS pairings. There is one today: **Lambda → Firecracker MicroVM**,
11riding the generic fetch transport — each call is a
12`POST https://<endpoint>/__rpc__/{name}`.
13
14## The image declares its Shape
15
16```typescript
17// sandbox.ts — the MicroVM image
18import * as AWS from "alchemy/AWS";
19import * as Effect from "effect/Effect";
20import * as HttpServerResponse from "effect/http/HttpServerResponse";
21
22export class Sandbox extends AWS.Lambda.MicrovmImage<
23 Sandbox,
24 {
25 hello: (message: string) => Effect.Effect<string>;
26 }
27>()("MicrovmSandbox") {}
28
29export default Sandbox.make(
30 { main: import.meta.filename /* buildRole, resources, cpuConfigurations */ },
31 Effect.gen(function* () {
32 return {
33 fetch: Effect.gen(function* () {
34 return HttpServerResponse.text("hello from effectful microvm");
35 }),
36 hello: (message: string) => Effect.succeed(`hello, ${message}!`),
37 };
38 }),
39);
40```
41
42Like Containers on Cloudflare, the MicroVM image declares its Shape as an
43explicit type parameter — the caller reaches it over the network, so nothing
44can be inferred from a binding. The constructor Effect returns `{ fetch, ...rpcs }`;
45the in-VM runtime serves the RPC methods over `/__rpc__/*` and falls through
46to `fetch` for everything else, so the same image answers both call surfaces.
47
48## Provision a VM and mint a token
49
50```typescript
51// orchestrator.ts — the Lambda binds the instance operations to the image
52const runMicrovm = yield* AWS.Lambda.RunMicrovm(Sandbox);
53const getMicrovm = yield* AWS.Lambda.GetMicrovm(Sandbox);
54const createAuthToken = yield* AWS.Lambda.CreateAuthToken(Sandbox);
55const terminateMicrovm = yield* AWS.Lambda.TerminateMicrovm(Sandbox);
56
57// ...inside a handler: run, wait for RUNNING, mint a token
58const vm = yield* runMicrovm({
59 idlePolicy: {
60 maxIdleDurationSeconds: 900,
61 suspendedDurationSeconds: 300,
62 autoResumeEnabled: true,
63 },
64});
65
66yield* getMicrovm({ microvmIdentifier: vm.microvmId }).pipe(
67 Effect.flatMap((m) =>
68 m.state === "RUNNING"
69 ? Effect.void
70 : Effect.fail(new Error(`microvm ${m.state}`)),
71 ),
72 Effect.retry({ schedule: Schedule.spaced("2 seconds"), times: 30 }),
73);
74
75const { authToken } = yield* createAuthToken({
76 microvmIdentifier: vm.microvmId,
77 expirationInMinutes: 5,
78 allowedPorts: [{ port: 8080 }],
79});
80```
81
82Unlike a service binding, a MicroVM has a lifecycle: the Lambda binds the
83instance operations (`RunMicrovm`, `GetMicrovm`, `CreateAuthToken`,
84`TerminateMicrovm`), launches an instance, polls with a bounded retry until it
85reaches `RUNNING`, and mints a short-lived auth token scoped to the RPC port.
86Wrap the launched instance in `Effect.ensuring(terminateMicrovm(...))` so a
87failing step never leaks a running VM. The full lifecycle — suspend, resume,
88list, the raw `fetch` route via `microvmAuthHeaders` — is in
89[MicroVMs](/aws/compute/microvms#launch-and-drive-microvms-from-a-lambda).
90
91## Connect and call
92
93```typescript
94const sandbox = yield* AWS.Lambda.connectMicrovm(Sandbox, {
95 endpoint: vm.endpoint,
96 authToken,
97});
98const reply = yield* sandbox.hello(message).pipe(
99 Effect.retry({
100 schedule: Schedule.exponential("500 millis"),
101 times: 8,
102 }),
103);
104// reply === `hello, ${message}!`
105```
106
107`connectMicrovm` returns the typed stub inferred from `Sandbox` — each method
108call is a `POST https://<endpoint>/__rpc__/<method>` with the auth-token
109entries set as request headers, the mirror of the server running inside the
110VM. Retry the first call: the endpoint takes a few seconds to start serving
111after the VM reports `RUNNING`.
112
113## What AWS doesn't have yet
114
115There is no schemaless stub between Lambda Functions — Lambda-to-Lambda calls
116go through the `AWS.Lambda.InvokeFunction` binding (see
117[Lambda](/aws/compute/lambda)) or a Function URL, and typed cross-Lambda APIs
118should use [Effect RPC](/apis/effect-rpc). There is also no typed connector
119for [ECS Tasks](/aws/compute/ecs) yet — a Task returns the same
120`{ fetch, ...rpcs }` Shape, but with no `connectMicrovm` equivalent you reach
121it over plain HTTP through its load balancer or serve Effect RPC from its
122`fetch` handler.
123
124## When you need schemas
125
126Schemaless calls do no runtime validation — nothing decodes or sanitizes what
127arrives. At a trust boundary, or when class identity must survive the hop, use
128[Effect RPC](/apis/effect-rpc).
129
130## Where next
131
132- [Schemaless RPC](/apis/schemaless) — the pattern: allowed members, the wire
133 codec, streams, errors
134- [MicroVMs](/aws/compute/microvms) — build images, drive instances, both call
135 surfaces
136- [Effect RPC](/apis/effect-rpc) — schema-first RPC for trust boundaries
137- [Cloudflare Schemaless RPC](/cloudflare/apis/schemaless-rpc) — the Cloudflare
138 pairings: Workers, Durable Objects, Containers