diff --git a/apps/content/content/docs/server/error-handling.mdx b/apps/content/content/docs/server/error-handling.mdx index 773bdc46..24003666 100644 --- a/apps/content/content/docs/server/error-handling.mdx +++ b/apps/content/content/docs/server/error-handling.mdx @@ -1,8 +1,70 @@ --- -title: Error Handling +title: Typesafe Error Handling description: How to intercept, handle or log errors inside oRPC --- +## Typed errors + +```ts twoslash +import { createORPCReactQueryUtils } from '@orpc/react-query' +import { createProcedureClient, isDefinedError, os, safe } from '@orpc/server' + +const createPost = os + .errors({ + CONFLICT: { + status: 409, // (optional) default CONFLICT is 409 + message: 'Conflict', // (optional) + data: z.object({ // (optional) + why: z.string(), + }), + }, + // ANY_CODE: { unknown code is allowed, with default status is 500 + // status: 500, + // }, + }) + .handler(({ errors }) => { + throw errors.CONFLICT({ data: { why: 'some reason' } }) + + // is the same as, but this is not typed + throw new ORPCError({ code: 'CONFLICT', data: { why: 'some reason' } }) + + // throw errors.ANY_CODE() + }) + +const client = createProcedureClient({ procedure: createPost }) // or any kind of client + +const [data, error, isDefined] = await safe(client({ title: 'title' })) + +if (error) { + if (isDefinedError(error)) { // or use isDefined const + const data = error.data // { why: 'some reason' } full typed data + } + else { + // any errors what not satisfy, or not defined in .errors + } +} + +/// example handle error for `@orpc/react-query` + +import { useMutation } from '@tanstack/react-query' +import { z } from 'zod' + +const utils = createORPCReactQueryUtils(client) + +const mutation = useMutation(utils.mutationOptions({ + onError(error) { + if (isDefinedError(error)) { + const data = error.data // { why: 'some reason' } full typed data + } + }, +})) +``` + +> **Note**: typesafe errors are coverage in every oRPC packages: `@orpc/client`, `@orpc/react-query`, `@orpc/vue-query`, `@orpc/vue-colada`, ... +> Please use `isDefinedError` or `safe` to typesafe handle errors. Export from `@orpc/contract`, `@orpc/server`, and `@orpc/client`. + +## Handle errors + ```ts twoslash import { ORPCError, os } from '@orpc/server' diff --git a/apps/content/content/home/landing.mdx b/apps/content/content/home/landing.mdx index 2f26ad60..a1bc374d 100644 --- a/apps/content/content/home/landing.mdx +++ b/apps/content/content/home/landing.mdx @@ -8,6 +8,13 @@ export const updateUser = os path: '/users/{id}' // dynamic params support method: 'PATCH' // custom OpenAPI method }) + .errors({ // End-to-end typesafe error handling + NOT_FOUND: { + data: z.object({ + why: z.string(), + }), + } + }) .input(z.object({ id: z.bigint(), user: z.object({ @@ -17,11 +24,15 @@ export const updateUser = os })) .output(z.literal("success")) // validate output .use(canMiddleware, (input) => input.id) // permission check by id - .handler(async ({ input }) => /* handle user update */) + .handler(async ({ input, errors }) => { + /* handle user update */ + + // or throw + throw errors.NOT_FOUND({ data: { why: 'some reason' } }) + }) ``` > Only the `.handler` method is required. All other chain methods are optional. -> With [Middleware](/docs/server/middleware) and the [Procedure Builder](/docs/server/procedure), you can create **reusable logic** that ensures **type safety** and adds **power** and **flexibility** to your functions.