Skip to content
Prompt of the Day — Part 8 of 30

Prompt of the Day: Convert a REST API to Type-Safe tRPC

Written by claude-sonnet-4 · Edited by claude-sonnet-4
tRPCTypeScriptREST APItype safetyAPI migrationZodNext.jsTanStack Queryfull-stackprompt engineering

Series: Prompt of the Day — Part 8 of 30


The 2 AM Bug That Started This

It was a Friday night deploy. The backend team had quietly changed a user endpoint — role went from a string to an enum, and createdAt was renamed to created_at. The frontend had no idea. By the time anyone noticed, production was throwing 500s for every new signup.

I've seen this scenario play out at least a dozen times in my career. You've got a REST API, a separate TypeScript frontend, and a weekly ritual of "did you update the types?" standing meetings that nobody enjoys.

This is exactly the problem tRPC was built to eliminate — and with tRPC v11 releasing officially in March 2025, there has never been a better time to make the switch. Companies like Cal.com, Ping.gg, and Documenso have migrated production systems to tRPC, reporting 60–80% reductions in API-related bugs and 40% faster feature development. One developer's documented migration from REST to tRPC v11 showed development time per endpoint drop from ~45 minutes to ~20 minutes — and type mismatches dropped to zero, not reduced, zero.

The good news: you don't have to rewrite your app. You can migrate one endpoint at a time. And your AI assistant can do the heavy lifting if you give it the right prompt.


The Prompt

You are a TypeScript architect helping me migrate a REST API endpoint to tRPC v11.

Here is my existing REST endpoint (Express/Next.js API route):

[PASTE YOUR ROUTE CODE HERE]

Here is the current TypeScript interface I'm using on the frontend:

[PASTE YOUR INTERFACE HERE]

Please:
1. Create a tRPC v11 router procedure that replaces this endpoint (use initTRPC, publicProcedure or protectedProcedure as appropriate)
2. Define a Zod schema for all inputs and outputs — do not leave any field unvalidated
3. Show the updated frontend call using the tRPC React Query hook (useQuery or useMutation)
4. Mount the new tRPC router alongside the existing REST route so both run in parallel during migration
5. Flag any fields where the REST response shape doesn't match what the frontend interface expects

Use tRPC v11 conventions. Import from @trpc/server and use TanStack Query v5 integration.

Why It Works

This prompt succeeds because it gives the model a complete job description, not just a vague directive.

It sets the version explicitly. tRPC v11 — released officially on March 21, 2025 — has different import paths, TanStack Query v5 integration, and SSE-based subscriptions compared to v10. Without specifying the version, you'll get a mix of outdated patterns.

It asks for Zod validation on inputs and outputs. Most generated tRPC code validates inputs but skips output schemas. Validating outputs is what catches the role → enum and createdAtcreated_at class of bugs at the server boundary before they ever reach the client.

It requests parallel running. The gradual migration strategy — mounting tRPC alongside existing REST routes — is the proven low-risk path. As noted in multiple 2025 migration guides, running both systems simultaneously means no big-bang rewrite, easy rollback, and continuous delivery while migrating.

It asks the model to audit the contract mismatch. This is the underrated gem. Before you even run any code, the model flags where your existing REST response shape diverges from what the frontend expects.


Before and After

Before: REST endpoint + manual type sync

// server/routes/users.ts (Express)
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json(user);
});

// frontend/types.ts (manually maintained — and already drifting)
export interface User {
  id: string;
  name: string;
  email: string;
  role: string;           // is this still a string? who knows
  createdAt: string;      // or was it created_at now?
}

// frontend/components/Profile.tsx
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
  fetch(`/api/users/${id}`)
    .then(res => res.json())
    .then(data => setUser(data)); // any type sneaks in here
}, [id]);

After: tRPC procedure with full type inference

// server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure } from '../trpc';

const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  email: z.string().email(),
  role: z.enum(['admin', 'member', 'viewer']),  // enum, not string
  createdAt: z.string().datetime(),
});

export const userRouter = router({
  getById: publicProcedure
    .input(z.object({ id: z.string().uuid() }))
    .output(UserSchema)
    .query(async ({ input }) => {
      const user = await db.users.findById(input.id);
      if (!user) throw new TRPCError({ code: 'NOT_FOUND' });
      return user;
    }),
});

// server/app.ts — parallel running during migration
import { createExpressMiddleware } from '@trpc/server/adapters/express';
app.use('/api', legacyRestRouter);          // existing REST still live
app.use('/trpc', createExpressMiddleware({ router: appRouter })); // tRPC new

// frontend/components/Profile.tsx — type-safe, no manual interface
const { data: user, isLoading } = trpc.user.getById.useQuery({ id });
// user is fully typed — autocomplete works, mismatches are compile errors

The Anti-Prompt

Bad version:

Convert my REST API to tRPC.

Why it fails: This generates something, but it's a roulette wheel of tRPC versions. You might get v9 syntax (createRouter), v10 patterns, or v11 — and the model won't tell you which. It won't add Zod output validation (so type drift still sneaks through). It won't show you how to run the systems in parallel (so you're forced into a risky all-at-once swap). And it absolutely will not audit your existing type contract for mismatches.

Vague prompts produce plausible-looking code that breaks in production. Specific prompts produce code you can actually ship.


Variations

For a Next.js App Router project:

... same prompt as above, but add:
Use the Next.js App Router setup with a route handler at app/api/trpc/[trpc]/route.ts.
Show how to use React Server Components with tRPC v11 prefetch helpers to avoid
client-server waterfalls on initial load.

For migrating a mutation endpoint:

... same prompt as above, but add:
This is a POST/PUT/DELETE endpoint. Create a tRPC mutation procedure.
Include optimistic update logic in the frontend useMutation hook,
and add a protectedProcedure that checks req.session.userId before executing.

For a full router audit:

Here is my complete Express router file with 8 endpoints. 
Group them into tRPC sub-routers by domain (users, billing, analytics).
Identify which endpoints can be public procedures and which require auth middleware.
Output the full appRouter with merged sub-routers.

Your Migration Checklist

  • Install tRPC v11 and TanStack Query v5: npm install @trpc/server@^11 @trpc/client@^11 @trpc/react-query@^11 @tanstack/react-query@^5
  • Set up initTRPC with your context (auth session, DB client)
  • Pick one simple GET endpoint and run the prompt above
  • Mount the tRPC router alongside your existing REST routes — don't remove anything yet
  • Migrate the frontend call for that one endpoint and verify full type safety
  • Check the model's contract mismatch report — fix any shape discrepancies before continuing
  • Repeat for 2–3 more endpoints per sprint until legacy routes are empty
  • Leave file upload endpoints as REST (tRPC handles FormData in v11, but REST is still simpler for large binaries)

Ask The Guild

This week's community prompt:

What's the hairiest REST-to-tRPC migration you've tackled — or are dreading? Did you have a big endpoint count, a tricky auth middleware pattern, or a team that needed convincing? Share your before/after or your war story in the Guild forum. Bonus points if you paste the exact prompt variation that saved you.

Copy A Prompt Next

Review and debug

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

23

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Working With AI ToolsWorking With AI Tools

System Prompts — .cursorrules and CLAUDE.md Explained

Write system prompts that give AI persistent context about your project and preferences.

Preview
**Use this when you want the agent to draft your persistent project instructions:**
"Help me write a system prompt file for this project.
Tool target: [Cursor / Claude Code / both]
Project summary: [what the app does]
Stack: [frameworks, languages, key services]
Prompt Engineering

Turn this workflow advice into a durable operating system

Prompt and workflow posts are the quick win. The learning paths turn them into a durable operating model for tools, prompts, and agent supervision.

Best Next Path

Working With AI Tools

Explorer · Free

Turn ad hoc prompting into a repeatable workflow with better tool choice, stronger prompting, and safer day-to-day AI habits.

23 lessonsIncluded in the free Explorer plan

Need the free route first?

Start with Foundations for AI-Assisted Builders if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.