Skip to content
tRPC

Adapters

One handler serves HTTP, SSE and WebSockets on every runtime; pick the adapter for yours.

On this page

createFetchHandler() returns a web-standard fetch(request). Anything that speaks Request and Response can serve it directly. On Effect's HttpServer, @trpc12/trpc/http adds WebSockets, client-disconnect aborts, and Effect's platform layers.

const handler = createFetchHandler({ router, createContext, layer });
// handler.fetch(request): Promise<Response>
// handler.websocket(socket, { request })
// handler.dispose(): releases the layer

Procedures are served under endpoint, /trpc by default.

RuntimeServe withWebSockets
Nodeserve(handler) on NodeHttpServer, or Expressyes
BunBun.serve({ fetch: handler.fetch })with serve(handler)
DenoDeno.servehandler.websocket
Workers, Next.jshandler.fetchin a Durable Object
AWS LambdatoLambdaHandler(handler)no
Durable Objectsexperimental_createHibernatingWebSocketshibernating
Message portshandler.messagePort(port)n/a
Firebasecall() in onCallno
Raw socketsserveSocket(handler)the WebSocket protocol over TCP

Middleware and CORS

Request-level concerns are Effect HttpMiddleware in the handler's middleware option, outermost first. They wrap handler.fetch and, under serve()/toHttpEffect, WebSocket upgrades too. Call-level concerns are root t.use() middleware.

import { HttpMiddleware } from 'effect/http';
import { cors } from '@trpc12/trpc/http';

const handler = createFetchHandler({
  router,
  middleware: [
    cors({ allowedOrigins: ['https://app.example.com'], credentials: true }),
    HttpMiddleware.logger,
  ],
});

cors() is Effect's HttpMiddleware.cors plus an origin check on WebSocket upgrades (403 for other origins). Browsers send cookies with cross-site upgrades and CORS doesn't cover them, so use it when you authenticate with cookies.

Compression

@trpc12/trpc doesn't compress. Your CDN or proxy (Cloudflare, Vercel, nginx) already negotiates Accept-Encoding, and does it better. If you add a compressing HttpMiddleware, skip application/jsonl, application/vnd.trpc.frames and text/event-stream responses: a gzip stream buffers until its window fills, which stalls subscriptions and streamed results.