Skip to content

Commit 0d235c8

Browse files
authored
docs: update Input/Output Structure & Validation Errors (#170)
* improve Input/Output Structure examples * Validation Errors
1 parent d5f0415 commit 0d235c8

File tree

2 files changed

+65
-33
lines changed

2 files changed

+65
-33
lines changed

apps/content/docs/advanced/validation-errors.md

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,81 @@ description: Learn about oRPC's built-in validation errors and how to customize
77

88
oRPC provides built-in validation errors that work well by default. However, you might sometimes want to customize them.
99

10+
## Customizing with Client Interceptors
11+
12+
[Client Interceptors](/docs/lifecycle) are preferred because they run before error validation, ensuring that your custom errors are properly validated.
13+
14+
```ts twoslash
15+
import { z } from 'zod'
16+
import { router } from './shared/planet'
17+
import { RPCHandler } from '@orpc/server/fetch'
18+
// ---cut---
19+
import { onError, ORPCError, ValidationError } from '@orpc/server'
20+
21+
const handler = new RPCHandler(router, {
22+
clientInterceptors: [
23+
onError((error) => {
24+
if (
25+
error instanceof ORPCError
26+
&& error.code === 'BAD_REQUEST'
27+
&& error.cause instanceof ValidationError
28+
) {
29+
throw new ORPCError('INPUT_VALIDATION_FAILED', {
30+
status: 422,
31+
data: { issues: error.cause.issues },
32+
cause: error.cause,
33+
})
34+
}
35+
36+
if (
37+
error instanceof ORPCError
38+
&& error.code === 'INTERNAL_SERVER_ERROR'
39+
&& error.cause instanceof ValidationError
40+
) {
41+
throw new ORPCError('OUTPUT_VALIDATION_FAILED', {
42+
cause: error.cause,
43+
})
44+
}
45+
})
46+
]
47+
})
48+
```
49+
1050
## Customizing with Middleware
1151

1252
```ts twoslash
1353
import { z } from 'zod'
1454
// ---cut---
15-
import { ORPCError, os, ValidationError } from '@orpc/server'
55+
import { onError, ORPCError, os, ValidationError } from '@orpc/server'
1656

17-
const base = os.use(async ({ next }) => {
18-
try {
19-
return await next()
57+
const base = os.use(onError((error) => {
58+
if (
59+
error instanceof ORPCError
60+
&& error.code === 'BAD_REQUEST'
61+
&& error.cause instanceof ValidationError
62+
) {
63+
throw new ORPCError('INPUT_VALIDATION_FAILED', {
64+
status: 422,
65+
data: { issues: error.cause.issues },
66+
cause: error.cause,
67+
})
2068
}
21-
catch (error) {
22-
// Customize input validation errors
23-
if (
24-
error instanceof ORPCError
25-
&& error.code === 'BAD_REQUEST'
26-
&& error.cause instanceof ValidationError
27-
) {
28-
throw new ORPCError('BAD_REQUEST', { // Customize your error
29-
data: { issues: error.cause.issues },
30-
cause: error.cause,
31-
})
32-
}
33-
34-
// Customize output validation errors
35-
if (
36-
error instanceof ORPCError
37-
&& error.code === 'INTERNAL_SERVER_ERROR'
38-
&& error.cause instanceof ValidationError
39-
) {
40-
throw new ORPCError('INTERNAL_SERVER_ERROR', { // Customize your error
41-
cause: error.cause,
42-
})
43-
}
44-
45-
throw error
69+
70+
if (
71+
error instanceof ORPCError
72+
&& error.code === 'INTERNAL_SERVER_ERROR'
73+
&& error.cause instanceof ValidationError
74+
) {
75+
throw new ORPCError('OUTPUT_VALIDATION_FAILED', {
76+
cause: error.cause,
77+
})
4678
}
47-
})
79+
}))
4880

4981
const getting = base
5082
.input(z.object({ id: z.string().uuid() }))
5183
.output(z.object({ id: z.string().uuid(), name: z.string() }))
52-
.handler(async ({ input }) => {
84+
.handler(async ({ input, context }) => {
5385
return { id: input.id, name: 'name' }
5486
})
5587
```

apps/content/docs/openapi/input-output-structure.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const detailedMode = os.route({
3939
params: z.object({ name: z.string() }),
4040
query: z.object({ search: z.string() }),
4141
body: z.object({ description: z.string() }).optional(),
42-
headers: z.object({ 'content-type': z.string() }),
42+
headers: z.object({ 'x-custom-header': z.string() }),
4343
}))
4444
```
4545

@@ -79,7 +79,7 @@ const detailedMode = os
7979
.route({ outputStructure: 'detailed' })
8080
.handler(async ({ input }) => {
8181
return {
82-
headers: { 'content-type': 'text/plain' },
82+
headers: { 'x-custom-header': 'value' },
8383
body: { message: 'Hello, world!' },
8484
}
8585
})

0 commit comments

Comments
 (0)