FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,249 @@
---
name: server-side-calls
description: >
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.
type: core
library: trpc
library_version: '11.15.1'
requires:
- server-setup
sources:
- 'trpc/trpc:www/docs/server/server-side-calls.md'
---
# tRPC -- Server-Side Calls
## Setup
```ts
// 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;
```
```ts
// 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);
```
```ts
// 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
```ts
// 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
```ts
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
```ts
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
```ts
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:
```ts
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:
```ts
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:
```ts
import { appRouter } from './appRouter';
const caller = appRouter.createCaller({});
await caller.protectedRoute();
// middleware throws UNAUTHORIZED because ctx.user is undefined
```
Correct:
```ts
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 configuration
- `middlewares` -- auth middleware that callers must satisfy
- `error-handling` -- TRPCError and getHTTPStatusCodeFromError