the problem
in a multi‑tenant SaaS we needed a single source of truth for data models while keeping page loads under 200 ms on the first paint. the backend team pushed a tRPC router for internal services, but the frontend product demanded flexible queries for dashboards, so we introduced a thin GraphQL layer on top of the same types. the result was a tangled contract surface that threatened both type safety and cacheability.
the api contract shape
using zod together with tRPC lets us export the exact schema to the client:
import { z } from ' zod ' import { initTRPC } from ' @trpc/server '
const t = initTRPC . create ()
const userSchema = z . object ({ id : z . string (), name : z . string (), role : z . enum ([ ' admin ' , ' member ' ]) })
export const appRouter = t . router ({ getUser : t . procedure . input ( z . string ()). output ( userSchema ). query (({ input }) => { return db . user .…