@trpc/server 11.14.0 → 11.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,228 @@
1
+ ---
2
+ name: validators
3
+ description: >
4
+ Configure input and output validation with .input() and .output() using Zod,
5
+ Yup, Superstruct, ArkType, Valibot, Effect, or custom validator functions.
6
+ Chain multiple .input() calls to merge object schemas. Standard Schema protocol
7
+ support. Output validation returns INTERNAL_SERVER_ERROR on failure.
8
+ type: core
9
+ library: trpc
10
+ library_version: '11.14.0'
11
+ requires:
12
+ - server-setup
13
+ sources:
14
+ - 'trpc/trpc:www/docs/server/validators.md'
15
+ - 'trpc/trpc:packages/server/src/unstable-core-do-not-import/parser.ts'
16
+ ---
17
+
18
+ # tRPC -- Validators
19
+
20
+ ## Setup
21
+
22
+ ```ts
23
+ // server/trpc.ts
24
+ import { initTRPC } from '@trpc/server';
25
+
26
+ const t = initTRPC.create();
27
+
28
+ export const router = t.router;
29
+ export const publicProcedure = t.procedure;
30
+ ```
31
+
32
+ ```ts
33
+ // server/appRouter.ts
34
+ import { z } from 'zod';
35
+ import { publicProcedure, router } from './trpc';
36
+
37
+ export const appRouter = router({
38
+ hello: publicProcedure
39
+ .input(z.object({ name: z.string() }))
40
+ .output(z.object({ greeting: z.string() }))
41
+ .query(({ input }) => {
42
+ return { greeting: `hello ${input.name}` };
43
+ }),
44
+ });
45
+
46
+ export type AppRouter = typeof appRouter;
47
+ ```
48
+
49
+ ## Core Patterns
50
+
51
+ ### Input validation with Zod
52
+
53
+ ```ts
54
+ import { z } from 'zod';
55
+ import { publicProcedure, router } from './trpc';
56
+
57
+ export const appRouter = router({
58
+ userById: publicProcedure.input(z.string()).query(({ input }) => {
59
+ return { id: input, name: 'Katt' };
60
+ }),
61
+ userCreate: publicProcedure
62
+ .input(z.object({ name: z.string(), email: z.string().email() }))
63
+ .mutation(({ input }) => {
64
+ return { id: '1', ...input };
65
+ }),
66
+ });
67
+ ```
68
+
69
+ ### Input chaining to merge object schemas
70
+
71
+ ```ts
72
+ import { initTRPC } from '@trpc/server';
73
+ import { z } from 'zod';
74
+
75
+ const t = initTRPC.create();
76
+
77
+ const baseProcedure = t.procedure
78
+ .input(z.object({ townName: z.string() }))
79
+ .use((opts) => {
80
+ console.log(`Request from: ${opts.input.townName}`);
81
+ return opts.next();
82
+ });
83
+
84
+ export const appRouter = t.router({
85
+ hello: baseProcedure
86
+ .input(z.object({ name: z.string() }))
87
+ .query(({ input }) => {
88
+ return { greeting: `Hello ${input.name}, from ${input.townName}` };
89
+ }),
90
+ });
91
+ ```
92
+
93
+ Multiple `.input()` calls merge object properties; the final input type is `{ townName: string; name: string }`.
94
+
95
+ ### Output validation
96
+
97
+ ```ts
98
+ import { z } from 'zod';
99
+ import { publicProcedure, router } from './trpc';
100
+
101
+ export const appRouter = router({
102
+ hello: publicProcedure
103
+ .output(z.object({ greeting: z.string() }))
104
+ .query(() => {
105
+ return { greeting: 'hello world' };
106
+ }),
107
+ });
108
+ ```
109
+
110
+ Output validation catches mismatches between your return type and the expected shape, useful for untrusted data sources.
111
+
112
+ ### Custom validator function (no library)
113
+
114
+ ```ts
115
+ import { initTRPC } from '@trpc/server';
116
+
117
+ const t = initTRPC.create();
118
+
119
+ export const appRouter = t.router({
120
+ hello: t.procedure
121
+ .input((value): string => {
122
+ if (typeof value === 'string') return value;
123
+ throw new Error('Input is not a string');
124
+ })
125
+ .output((value): string => {
126
+ if (typeof value === 'string') return value;
127
+ throw new Error('Output is not a string');
128
+ })
129
+ .query(({ input }) => {
130
+ return `hello ${input}`;
131
+ }),
132
+ });
133
+ ```
134
+
135
+ ## Common Mistakes
136
+
137
+ ### [MEDIUM] Chaining non-object inputs
138
+
139
+ Wrong:
140
+
141
+ ```ts
142
+ import { z } from 'zod';
143
+ import { publicProcedure } from './trpc';
144
+
145
+ const proc = publicProcedure.input(z.string()).input(z.number());
146
+ ```
147
+
148
+ Correct:
149
+
150
+ ```ts
151
+ import { z } from 'zod';
152
+ import { publicProcedure } from './trpc';
153
+
154
+ const proc = publicProcedure
155
+ .input(z.object({ name: z.string() }))
156
+ .input(z.object({ age: z.number() }));
157
+ ```
158
+
159
+ Multiple `.input()` calls merge object properties; non-object schemas (string, number, array) cannot be merged and produce type errors.
160
+
161
+ Source: www/docs/server/validators.md
162
+
163
+ ### [MEDIUM] Output validation failure returns 500
164
+
165
+ Wrong:
166
+
167
+ ```ts
168
+ import { z } from 'zod';
169
+ import { publicProcedure } from './trpc';
170
+
171
+ const proc = publicProcedure
172
+ .output(z.object({ id: z.string() }))
173
+ .query(() => ({ id: 123 }));
174
+ ```
175
+
176
+ Correct:
177
+
178
+ ```ts
179
+ import { z } from 'zod';
180
+ import { publicProcedure } from './trpc';
181
+
182
+ const proc = publicProcedure
183
+ .output(z.object({ id: z.string() }))
184
+ .query(() => ({ id: '123' }));
185
+ ```
186
+
187
+ If `.output()` validation fails, tRPC returns INTERNAL_SERVER_ERROR (500), not BAD_REQUEST, because the server produced invalid data.
188
+
189
+ Source: www/docs/server/validators.md
190
+
191
+ ### [HIGH] Using cursor: z.optional() without nullable for infinite queries
192
+
193
+ Wrong:
194
+
195
+ ```ts
196
+ import { z } from 'zod';
197
+ import { publicProcedure } from './trpc';
198
+
199
+ const proc = publicProcedure
200
+ .input(z.object({ cursor: z.string().optional() }))
201
+ .query(({ input }) => {
202
+ return { items: [], nextCursor: input.cursor };
203
+ });
204
+ ```
205
+
206
+ Correct:
207
+
208
+ ```ts
209
+ import { z } from 'zod';
210
+ import { publicProcedure } from './trpc';
211
+
212
+ const proc = publicProcedure
213
+ .input(z.object({ cursor: z.string().nullish() }))
214
+ .query(({ input }) => {
215
+ return { items: [], nextCursor: input.cursor };
216
+ });
217
+ ```
218
+
219
+ React Query internally passes `cursor: undefined` during invalidation refetch; using `.optional()` without `.nullable()` can fail validation. Use `.nullish()` instead.
220
+
221
+ Source: https://github.com/trpc/trpc/issues/6862
222
+
223
+ ## See Also
224
+
225
+ - `server-setup` -- initTRPC, routers, procedures
226
+ - `error-handling` -- how validation errors surface as BAD_REQUEST
227
+ - `error-handling` -- errorFormatter to expose Zod field errors
228
+ - `middlewares` -- use input chaining with middleware base procedures