server-side-calls
Call tRPC procedures directly from server code using t.createCallerFactory() and router.createCaller(context) for integration testing, internal server logic, and custom API endpoints. Catch TRPCError and extract HTTP status with getHTTPStatusCodeFromError(). Error handling via onError option.
- 0
- Installs
- —
- Rating
- —
- Success rate
- 1
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 7788d28f5aac88e1… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
SKILL.md
tRPC -- Server-Side Calls
Setup
// server/trpc.ts
import { initTRPC } from '@trpc/server';
type Context = { user?: { id: string } };
const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const createCallerFactory = t.createCallerFactory;
// server/appRouter.ts
import { TRPCError } from '@trpc/server';
import { z } from 'zod';
import { createCallerFactory, publicProcedure, router } from './trpc';
interface Post {
id: string;
title: string;
}
const posts: Post[] = [{ id: '1', title: 'Hello world' }];
export const appRouter = router({
post: router({
add: publicProcedure
.input(z.object({ title: z.string().min(2) }))
.mutation(({ input }) => {
const post: Post = { ...input, id: `${Math.random()}` };
posts.push(post);
return post;
}),
byId: publicProcedure
.input(z.object({ id: z.string() }))
.query(({ input }) => {
const post = posts.find((p) => p.id === input.id);
if (!post) throw new TRPCError({ code: 'NOT_FOUND' });
return post;
}),
list: publicProcedure.query(() => posts),
}),
});
export type AppRouter = typeof appRouter;
export const createCaller = createCallerFactory(appRouter);
// usage
import { createCaller } from './appRouter';
const caller = createCaller({ user: { id: '1' } });
const postList = await caller.post.list();
const newPost = await caller.post.add({ title: 'New post' });
Core Patterns
Integration test with createCallerFactory
// server/appRouter.test.ts
import type { inferProcedureInput } from '@trpc/server';
import { createCaller } from './appRouter';
import type { AppRouter } from './appRouter';
import { createContextInner } from './context';
async function testAddAndGetPost() {
const ctx = await createContextInner({ user: undefined });
const caller = createCaller(ctx);
const input: inferProcedureInput<AppRouter['post']['add']> = {
title: 'Test post',
};
const post = await caller.post.add(input);
const allPosts = await caller.post.list();
console.assert(allPosts.some((p) => p.id === post.id));
}
Using router.createCaller() directly
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello ${input.name}`),
});
const caller = appRouter.createCaller({});
const result = await caller.greeting({ name: 'tRPC' });
Error handling in a custom API endpoint
import { TRPCError } from '@trpc/server';
import { getHTTPStatusCodeFromError } from '@trpc/server/http';
import type { NextApiRequest, NextApiResponse } from 'next';
import { appRouter } from './appRouter';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const caller = appRouter.createCaller({});
try {
const post = await caller.post.byId({ id: req.query.id as string });
res.status(200).json({ data: { postTitle: post.title } });
} catch (cause) {
if (cause instanceof TRPCError) {
const httpStatusCode = getHTTPStatusCodeFromError(cause);
res.status(httpStatusCode).json({ error: { message: cause.message } });
return;
}
res.status(500).json({ error: { message: 'Internal server error' } });
}
}
Caller with onError callback
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => {
if (input.name === 'invalid') {
throw new Error('Invalid name');
}
return `Hello ${input.name}`;
}),
});
const caller = appRouter.createCaller(
{},
{
onError: (opts) => {
console.error('An error occurred:', opts.error);
},
},
);
Common Mistakes
[HIGH] Using createCaller inside another procedure
Wrong:
import { appRouter } from './appRouter';
import { createCallerFactory, publicProcedure } from './trpc';
const createCaller = createCallerFactory(appRouter);
const proc = publicProcedure.query(async () => {
const caller = createCaller({});
return caller.post.list();
});
Correct:
import type { Context } from './context';
import { publicProcedure } from './trpc';
async function listPosts(ctx: Context) {
return ctx.db.post.findMany();
}
const proc = publicProcedure.query(async ({ ctx }) => {
return listPosts(ctx);
});
Calling createCaller from within a procedure re-creates context, re-runs all middleware, and re-validates input; extract shared logic into a plain function instead.
Source: www/docs/server/server-side-calls.md
[MEDIUM] Not providing context to createCaller
Wrong:
import { appRouter } from './appRouter';
const caller = appRouter.createCaller({});
await caller.protectedRoute();
// middleware throws UNAUTHORIZED because ctx.user is undefined
Correct:
import { appRouter } from './appRouter';
import { createContextInner } from './context';
const ctx = await createContextInner({ user: { id: '1' } });
const caller = appRouter.createCaller(ctx);
await caller.protectedRoute();
createCaller requires a context object matching what procedures and middleware expect; passing an empty object when procedures require auth context causes runtime errors.
Source: www/docs/server/server-side-calls.md
See Also
server-setup-- initTRPC, routers, context configurationmiddlewares-- auth middleware that callers must satisfyerror-handling-- TRPCError and getHTTPStatusCodeFromError
Files
1- SKILL.md
007a1518626.1 KB
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from trpc/trpc8
Deploy tRPC on AWS Lambda with awsLambdaRequestHandler() from @trpc/server/adapters/aws-lambda for API Gateway v1 (REST, APIGatewayProxyEvent) and v2 (HTTP, APIGatewayProxyEventV2), and Lambda Function URLs. Enable response streaming with awsLambdaStreamingRequestHandler() wrapped in awslambda.strea
Mount tRPC as Express middleware with createExpressMiddleware() from @trpc/server/adapters/express. Access Express req/res in createContext via CreateExpressContextOptions. Mount at a path prefix like app.use('/trpc', ...). Avoid global express.json() conflicting with tRPC body parsing for FormData.
Mount tRPC as a Fastify plugin with fastifyTRPCPlugin from @trpc/server/adapters/fastify. Configure prefix, trpcOptions (router, createContext, onError). Enable WebSocket subscriptions with useWSS and @fastify/websocket. Set routerOptions.maxParamLength for batch requests. Requires Fastify v5+. Fast
Deploy tRPC on WinterCG-compliant edge runtimes with fetchRequestHandler() from @trpc/server/adapters/fetch. Supports Cloudflare Workers, Deno Deploy, Vercel Edge Runtime, Astro, Remix, SolidStart. FetchCreateContextFnOptions provides req (Request) and resHeaders (Headers) for context creation. The
Mount tRPC on Node.js built-in HTTP server with createHTTPServer() from @trpc/server/adapters/standalone, createHTTPHandler() for custom http.createServer, createHTTP2Handler() for HTTP/2 with TLS. Configure basePath to slice URL prefix, CORS via the cors npm package passed as middleware option. Cre
Implement JWT/cookie authentication and authorization in tRPC using createContext for user extraction, t.middleware with opts.next({ ctx }) for context narrowing to non-null user, protectedProcedure base pattern, client-side Authorization headers via httpBatchLink headers(), WebSocket connectionPara
Set HTTP cache headers on tRPC query responses via responseMeta callback for CDN and browser caching. Configure Cache-Control, s-maxage, stale-while-revalidate. Handle caching with batching and authenticated requests. Avoid caching mutations, errors, and authenticated responses.
Create a vanilla tRPC client with createTRPCClient<AppRouter>(), configure link chain with httpBatchLink/httpLink, dynamic headers for auth, transformer on links (not client constructor). Infer types with inferRouterInputs and inferRouterOutputs. AbortController signal support. TRPCClientError typin
Related backend skillsscan passed
PostHog error tracking for Ruby on Rails
Build MCP servers with Node/TypeScript SDK — tools, resources, prompts, Zod validation, stdio vs Streamable HTTP. Use Context7 or official MCP docs for latest API. Use when building or debugging an MCP server — tools, resources, prompts, validation, or transport choice.
Report browser/API/CLI/job/worker/webhook bugs. (gstack)
This skill should be used when the user asks to "build an MCP server", "create an MCP", "make an MCP integration", "wrap an API for Claude", "expose tools to Claude", "make an MCP app", or discusses building something with the Model Context Protocol. It is the entry point for MCP server development
Identifies external providers, merchants, nonprofits, platforms, APIs, and software services, and resolves the documented way to engage them — to pay, donate, subscribe, book, provision, or integrate with them. MUST be used BEFORE web search, model memory, or any other directory/vendor-lookup skill
Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.