tRPC lets you build end-to-end type-safe APIs without code generation. Version 11 brings server-sent events, streaming, and a new link architecture. Most developers only scratch the surface of what tRPC can do.
What's New in tRPC v11?
- SSE support — real-time subscriptions via Server-Sent Events
- Streaming — progressive data loading
- New link system — composable middleware for the client
- FormData — native form handling
The Hidden API: Advanced Patterns
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<Context>().create();
// Middleware with typed context augmentation
const authMiddleware = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
return next({ ctx: { ...ctx, user: ctx.user } });
});
const protectedProcedure = t.procedure.use(authMiddleware);
// Streaming responses
const appRouter = t.router({
// Standard query
user: protectedProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input, ctx }) => {
return ctx.db.user.findUnique({ where: { id: input.id } });
}),
// SSE subscription (new in v11)
onNewMessage: protectedProcedure
.subscription(async function* ({ ctx }) {
for await (const msg of ctx.messageStream) {
yield msg;
}
}),
// Streaming query
searchResults: t.procedure
.input(z.object({ query: z.string() }))
.query(async function* ({ input }) {
const results = await searchEngine(input.query);
for (const result of results) {
yield result; // Stream results as they come
}
}),
});
Client-Side Link API
import { createTRPCClient, httpBatchLink, splitLink, httpSubscriptionLink } from '@trpc/client';
const client = createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => op.type === 'subscription',
true: httpSubscriptionLink({ url: '/api/trpc' }),
false: httpBatchLink({ url: '/api/trpc' }),
}),
],
});
// Type-safe calls — autocomplete everything
const user = await client.user.query({ id: '123' });
// Real-time subscription
const unsub = client.onNewMessage.subscribe(undefined, {
onData: (msg) => console.log('New:', msg),
});
React Query Integration
import { createTRPCReact } from '@trpc/react-query';
const trpc = createTRPCReact<AppRouter>();
function UserProfile({ id }: { id: string }) {
const { data, isLoading } = trpc.user.useQuery({ id });
// Optimistic updates
const utils = trpc.useUtils();
const mutation = trpc.updateUser.useMutation({
onSuccess: () => utils.user.invalidate({ id }),
});
return data ? <div>{data.name}</div> : <Spinner />;
}
Quick Start
npm install @trpc/server @trpc/client @trpc/react-query
Why Teams Love tRPC
A full-stack developer shared: "We deleted our entire OpenAPI spec, all codegen scripts, and 200+ lines of type definitions. tRPC gives us end-to-end type safety with zero code generation. Refactoring an API endpoint shows errors in the client instantly."
Building full-stack apps? Email spinov001@gmail.com or check my developer tools.
Using tRPC? How do you handle API type safety?
Top comments (0)