Skip to content

tRPC v12 isn’t on npm yet. The @trpc12/* packages on this site can’t be installed until it’s released. Use v11 today

tRPC

Fetch runtimes

Cloudflare Workers, Next.js route handlers and other fetch runtimes take handler.fetch as it is.

On this page

Cloudflare Workers, Next.js route handlers, Lambda function URLs and other fetch runtimes take handler.fetch as it is.

// Cloudflare Workers
export default { fetch: (request: Request) => Promise<Response>fetch: const handler: FetchHandler<undefined>handler.FetchHandler<undefined>.fetch: (request: Request) => Promise<Response>fetch };

// Next.js: app/api/trpc/[[...trpc]]/route.ts (batches POST to /api/trpc itself),
// with createFetchHandler({ router, endpoint: '/api/trpc' })
export const const GET: (request: Request) => Promise<Response>GET = const handler: FetchHandler<undefined>handler.FetchHandler<undefined>.fetch: (request: Request) => Promise<Response>fetch;
export const const POST: (request: Request) => Promise<Response>POST = const handler: FetchHandler<undefined>handler.FetchHandler<undefined>.fetch: (request: Request) => Promise<Response>fetch;

Platform arguments

On Workers, env and ctx come with each request, not with the handler. Pass them as the platform argument, typed by the handler's second type parameter; createContext reads them:

const 
const handler: FetchHandler<{
    env: Env;
    ctx: ExecutionContext;
}>
handler
=
createFetchHandler<{
    health: Procedure<{
        type: "query";
        ctx: {
            db: D1Database;
            waitUntil: (p: Promise<unknown>) => void;
        };
        meta: {};
        input: undefined;
        output: string;
        errors: never;
        services: never;
    }>;
}, {
    env: Env;
    ctx: ExecutionContext;
}>(opts: FetchHandlerOptions<{
    health: Procedure<{
        type: "query";
        ctx: {
            db: D1Database;
            waitUntil: (p: Promise<unknown>) => void;
        };
        meta: {};
        input: undefined;
        output: string;
        errors: never;
        services: never;
    }>;
}, {
    env: Env;
    ctx: ExecutionContext;
}>): FetchHandler<{
    env: Env;
    ctx: ExecutionContext;
}>
createFetchHandler
<
type AppRouter = {
    health: Procedure<{
        type: "query";
        ctx: {
            db: D1Database;
            waitUntil: (p: Promise<unknown>) => void;
        };
        meta: {};
        input: undefined;
        output: string;
        errors: never;
        services: never;
    }>;
}
AppRouter
,
{ env: Envenv: Env; ctx: ExecutionContext<unknown>ctx: interface ExecutionContext<Props = unknown>ExecutionContext } >({
BaseHandlerOptions<{ health: Procedure<{ type: "query"; ctx: { db: D1Database; waitUntil: (p: Promise<unknown>) => void; }; meta: {}; input: undefined; output: string; errors: never; services: never; }>; }>.router: {
    health: Procedure<{
        type: "query";
        ctx: {
            db: D1Database;
            waitUntil: (p: Promise<unknown>) => void;
        };
        meta: {};
        input: undefined;
        output: string;
        errors: never;
        services: never;
    }>;
}
router
,
createContext: (opts: CreateContextOpts<{
    env: Env;
    ctx: ExecutionContext;
}>) => MaybePromise<{
    db: D1Database;
    waitUntil: (p: Promise<unknown>) => void;
}>
createContext
: (
opts: CreateContextOpts<{
    env: Env;
    ctx: ExecutionContext;
}>
opts
) => ({
db: D1Databasedb:
opts: CreateContextOpts<{
    env: Env;
    ctx: ExecutionContext;
}>
opts
.
CreateContextOpts<{ env: Env; ctx: ExecutionContext; }>.platform: {
    env: Env;
    ctx: ExecutionContext;
}
platform
.env: Envenv.Env.DB: D1DatabaseDB,
waitUntil: (p: Promise<unknown>) => voidwaitUntil: (p: Promise<unknown>p: interface Promise<T>Promise<unknown>) =>
opts: CreateContextOpts<{
    env: Env;
    ctx: ExecutionContext;
}>
opts
.
CreateContextOpts<{ env: Env; ctx: ExecutionContext; }>.platform: {
    env: Env;
    ctx: ExecutionContext;
}
platform
.ctx: ExecutionContext<unknown>ctx.ExecutionContext<unknown>.waitUntil(promise: Promise<any>): voidwaitUntil(p: Promise<unknown>p),
}), }); export default { ExportedHandler<Env, unknown, unknown, unknown>.fetch?: ExportedHandlerFetchHandler<Env, unknown, unknown> | undefinedfetch: (request: Request<unknown, IncomingRequestCfProperties<unknown>>request, env: Envenv, ctx: ExecutionContext<unknown>ctx) =>
const handler: FetchHandler<{
    env: Env;
    ctx: ExecutionContext;
}>
handler
.
FetchHandler<{ env: Env; ctx: ExecutionContext; }>.fetch: (request: Request, platform: {
    env: Env;
    ctx: ExecutionContext;
}) => Promise<Response>
fetch
(request: Request<unknown, IncomingRequestCfProperties<unknown>>request, { env: Envenv, ctx: ExecutionContext<unknown>ctx }),
} satisfies interface ExportedHandler<Env = unknown, QueueHandlerMessage = unknown, CfHostMetadata = unknown, Props = unknown>ExportedHandler<Env>; // in a resolver: ctx.waitUntil(sendWelcomeEmail(user))

With a platform type, handler.fetch(request) without it is a type error, and so is handler.websocket(socket, { request }) without platform. Without one, fetch takes the request alone, which is what Next's route handler types expect. serve(), Node and Bun pass nothing, and platform is undefined.

Effect middleware on a fetch runtime

For Effect HTTP middleware in front of the handler on a fetch runtime, convert the Effect app back to a web handler:

import { import HttpEffectHttpEffect } from 'effect/http';
import { function toHttpEffect(handler: FetchLike | (FetchLike & WebSocketHandlerLike), opts?: HttpOptions): Effect<HttpServerResponse, HttpServerError, HttpServerRequest>toHttpEffect } from '@trpc12/trpc/http';

const const fetch: (request: Request, context?: Context<never> | undefined) => Promise<globalThis.Response>fetch = import HttpEffectHttpEffect.const toWebHandler: <HttpServerError>(self: Effect<HttpServerResponse, HttpServerError, HttpServerRequest | Scope>, middleware?: HttpMiddleware | undefined) => (request: Request, context?: Context<never> | undefined) => Promise<globalThis.Response>toWebHandler(function toHttpEffect(handler: FetchLike | (FetchLike & WebSocketHandlerLike), opts?: HttpOptions): Effect<HttpServerResponse, HttpServerError, HttpServerRequest>toHttpEffect(const handler: FetchHandler<undefined>handler));

WebSockets on Workers go through a Durable Object.