ProvenPin Install

tRPC 11 client + server over fetch with superjson (Vitest): working versions (tested 2026-10-15)

End-to-end typed tRPC 11: fetch adapter server, createTRPCClient with httpBatchLink and superjson transformer for Dates and Maps, tested in-process with Vitest.

passed3 passed · 0 failed2026-10-15 13:34 UTC
Status
passed
Last passed
2026-10-15 13:34 UTC
Tests
3 / 3
Language
TypeScript
Runtime
node v22.23.3
pinned versions6
@trpc/server@11.19.0@trpc/client@11.19.0superjson@2.2.6zod@4.6.7@types/node@24.19.2vitest@5.0.4
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38742087302

The code

Exactly the files that passed. Your agent can pull them with get_recipe("trpc-client-server-superjson") (install), or see them on GitHub.

package.json
{
  "name": "trpc-client-server-superjson",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "vitest run --reporter=default --reporter=json --outputFile=test-report.json"
  },
  "dependencies": {
    "@trpc/server": "11.19.0",
    "@trpc/client": "11.19.0",
    "superjson": "2.2.6",
    "zod": "4.6.7"
  },
  "devDependencies": {
    "@types/node": "24.19.2",
    "vitest": "5.0.4"
  }
}
src/server.ts
import { initTRPC } from "@trpc/server";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import superjson from "superjson";
import { z } from "zod";

const t = initTRPC.create({ transformer: superjson });
const events: { name: string; at: Date }[] = [];

export const appRouter = t.router({
  addEvent: t.procedure.input(z.object({ name: z.string().min(1), at: z.date() })).mutation(({ input }) => {
    events.push(input);
    return { count: events.length };
  }),
  nextEvent: t.procedure.query(() => events.toSorted((a, b) => a.at.getTime() - b.at.getTime())[0] ?? null),
  tally: t.procedure.query(() => new Map(events.map((e) => [e.name, e.at]))),
});
export type AppRouter = typeof appRouter;

export const handler = (req: Request) => fetchRequestHandler({ endpoint: "/trpc", req, router: appRouter, createContext: () => ({}) });
src/client.ts
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import superjson from "superjson";
import type { AppRouter } from "./server";

// transformer goes on the link in tRPC 11 (not on createTRPCClient)
export const makeClient = (url: string, fetchImpl?: typeof fetch) =>
  createTRPCClient<AppRouter>({ links: [httpBatchLink({ url, transformer: superjson, fetch: fetchImpl })] });
test/trpc.test.ts
import { describe, it, expect, vi } from "vitest";
import { TRPCClientError } from "@trpc/client";
import { handler } from "../src/server";
import { makeClient } from "../src/client";

const fetchSpy = vi.fn((input: RequestInfo | URL, init?: RequestInit) => handler(new Request(input, init)));
const client = makeClient("http://local/trpc", fetchSpy as unknown as typeof fetch);

describe("trpc over fetch", () => {
  it("round-trips Dates and Maps", async () => {
    const at = new Date("2026-11-01T10:00:00Z");
    await client.addEvent.mutate({ name: "launch", at });
    const next = await client.nextEvent.query();
    expect(next?.at).toBeInstanceOf(Date);
    expect(next?.at.toISOString()).toBe(at.toISOString());
    const tally = await client.tally.query();
    expect(tally).toBeInstanceOf(Map);
    expect(tally.get("launch")).toBeInstanceOf(Date);
  });

  it("batches parallel calls into one request", async () => {
    fetchSpy.mockClear();
    await Promise.all([client.nextEvent.query(), client.tally.query()]);
    expect(fetchSpy).toHaveBeenCalledTimes(1);
  });

  it("returns typed validation errors", async () => {
    const err = await client.addEvent.mutate({ name: "", at: new Date() }).catch((e) => e);
    expect(err).toBeInstanceOf(TRPCClientError);
    expect(err.data?.code).toBe("BAD_REQUEST");
  });
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.