| 1 | --- |
| 2 | title: APIs |
| 3 | description: Every Function and Server returns { fetch, ...rpcs } — schemaless typed calls are the default for internal communication; Effect RPC and Effect HTTP add schemas where data crosses a trust boundary. |
| 4 | --- |
| 5 | |
| 6 | Every Function and Server in alchemy returns `{ fetch, ...rpcs }` from its |
| 7 | [Effectful Constructor](/infrastructure-as-effects/runtime#the-effectful-constructor) — |
| 8 | you already saw the "return what you expose" half. The `rpcs` are the other |
| 9 | half: plain functions returning `Effect` or `Stream`, no schema, no |
| 10 | decorators: |
| 11 | |
| 12 | ```typescript |
| 13 | import * as Cloudflare from "alchemy/Cloudflare"; |
| 14 | import * as Effect from "effect/Effect"; |
| 15 | import * as HttpServerResponse from "effect/http/HttpServerResponse"; |
| 16 | |
| 17 | export default class BindingTargetWorker extends Cloudflare.Worker<BindingTargetWorker>()( |
| 18 | "BindingTargetWorker", |
| 19 | { |
| 20 | main: import.meta.url, |
| 21 | }, |
| 22 | Effect.gen(function* () { |
| 23 | return { |
| 24 | greet: (name: string) => Effect.succeed(`hello ${name}`), |
| 25 | fetch: Effect.gen(function* () { |
| 26 | return HttpServerResponse.text("hello from BindingTargetWorker"); |
| 27 | }), |
| 28 | }; |
| 29 | }), |
| 30 | ) {} |
| 31 | ``` |
| 32 | |
| 33 | Any other resource that binds this Worker gets a **fully typed client**: |
| 34 | `yield* target.greet("world")` is an ordinary Effect call, the return type is |
| 35 | inferred from the class, and serialization is handled automatically. The same |
| 36 | model holds across every pairing — a Durable Object bound into a Worker, a |
| 37 | Container called from a Durable Object, a MicroVM called from a Lambda |
| 38 | Function. |
| 39 | |
| 40 | That schemaless path is the **default for internal communication**. Schemas |
| 41 | enter only when data crosses a **trust boundary** — a web app, an external |
| 42 | service, anything you don't deploy yourself. |
| 43 | |
| 44 | ## The three modalities |
| 45 | |
| 46 | | Modality | Schema | Per-request cost | Use when | Clients | |
| 47 | | ----------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | |
| 48 | | [Schemaless RPC](/apis/schemaless) | None | Proxy dispatch + structured clone (native Bindings) or JSON args (fetch transport) + an envelope-tag check — zero validation | Internal service-to-service: Worker → Worker, Worker → Durable Object, Durable Object → Container, Lambda → MicroVM | Any resource that binds the class (MicroVMs connect explicitly via `connectMicrovm`) | |
| 49 | | [Effect RPC](/apis/effect-rpc) | `RpcGroup` | Wire-frame parse + Schema decode of the payload + handler + Schema encode of the result — mirrored on the client | Data needs sanitizing or crosses a trust boundary (web app, external service) and consumers are Effect/TypeScript | The schema + a reference to the Fetcher — a Binding or just a URL | |
| 50 | | [Effect HTTP](/apis/effect-http) | `HttpApi` | Same profile as Effect RPC | Same trust-boundary case, but consumers are non-Effect / non-TypeScript | Plain HTTP — real URLs, path params, query strings, headers, content types, `curl` | |
| 51 | |
| 52 | ## Choosing |
| 53 | |
| 54 | Default to **Schemaless RPC for anything internal**. There is no schema to |
| 55 | write, no runtime checking, and no per-request validation cost — and internal |
| 56 | callers already get end-to-end types for free, because the client type *is* |
| 57 | the class you bound. |
| 58 | |
| 59 | **Effect RPC and Effect HTTP are for trust boundaries.** They are discouraged |
| 60 | for internal service-to-service calls: every request pays a Schema decode and |
| 61 | encode on both sides, which buys you nothing when you control both ends. Reach |
| 62 | for them when data needs sanitizing — a browser, a partner service, anything |
| 63 | outside your deploy. |
| 64 | |
| 65 | Between the two schema'd modalities the choice is about the **external |
| 66 | consumer**: an Effect/TypeScript client combines your schema with a Fetcher |
| 67 | and gets a typed RPC client — use [Effect RPC](/apis/effect-rpc); anyone else |
| 68 | wants a real HTTP surface they can hit with a plain HTTP client — use |
| 69 | [Effect HTTP](/apis/effect-http). |
| 70 | |
| 71 | The three interoperate on one host: the schema'd modalities build an |
| 72 | `HttpEffect` returned from `fetch`, so a single Worker or Function can serve |
| 73 | schemaless methods internally **and** a schema'd surface externally. |
| 74 | |
| 75 | ## Where next |
| 76 | |
| 77 | 1. [Schemaless RPC](/apis/schemaless) — the internal default: typed clients, Streams, and error propagation with no schema. |
| 78 | 2. [Effect RPC](/apis/effect-rpc) — `RpcGroup` schemas, handler Layers, and clients built from the schema + a Fetcher. |
| 79 | 3. [Effect HTTP](/apis/effect-http) — the same idea as real REST endpoints for non-Effect consumers. |
| 80 | |
| 81 | Then see it wired up in the hubs: |
| 82 | |
| 83 | - **Cloudflare** — [Schemaless RPC](/cloudflare/apis/schemaless-rpc), [Effect RPC](/cloudflare/apis/effect-rpc), [Effect HTTP](/cloudflare/apis/effect-http-api) |
| 84 | - **AWS** — [Schemaless RPC](/aws/apis/schemaless-rpc), [Effect RPC](/aws/apis/effect-rpc), [Effect HTTP](/aws/apis/effect-http-api) |