Skip to content

Commit

Permalink
feat: add errorformatter (#1273)
Browse files Browse the repository at this point in the history
* add errorformatter

* add docs

* changeset

* Update www/src/pages/en/usage/trpc.md

Co-authored-by: Christopher Ehrlich <[email protected]>

---------

Co-authored-by: Christopher Ehrlich <[email protected]>
Co-authored-by: Shoubhit Dash <[email protected]>
  • Loading branch information
3 people committed Mar 16, 2023
1 parent ae5cd40 commit 10a5e0b
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-masks-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-t3-app": minor
---

feat: add errorformatter for zod errors
3 changes: 2 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"superjson": "^1.12.2",
"tsup": "^6.6.3",
"type-fest": "^3.6.0",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"zod": "^3.21.4"
}
}
16 changes: 13 additions & 3 deletions cli/template/extras/src/server/api/trpc/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,25 @@ export const createTRPCContext = (_opts: CreateNextContextOptions) => {
/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer.
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/
import { initTRPC } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

Expand Down
16 changes: 13 additions & 3 deletions cli/template/extras/src/server/api/trpc/with-auth-prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,25 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer.
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

Expand Down
16 changes: 13 additions & 3 deletions cli/template/extras/src/server/api/trpc/with-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,25 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer.
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

Expand Down
16 changes: 13 additions & 3 deletions cli/template/extras/src/server/api/trpc/with-prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,25 @@ export const createTRPCContext = (_opts: CreateNextContextOptions) => {
/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer.
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/
import { initTRPC } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape }) {
return shape;
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions www/src/pages/en/usage/trpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,36 @@ const UserPage = () => {

You'll immediately notice how good the autocompletion and typesafety is. As soon as you write `api.`, your routers will show up in autocomplete, and when you select a router, its procedures will show up as well. You'll also get a TypeScript error if your input doesn't match the validator that you defined on the backend.

## Inferring errors

By default, `create-t3-app` sets up an [error formatter](https://trpc.io/docs/error-formatting) that lets you infer your Zod Errors if you get validation errors on the backend.

Example usage:

```tsx
function MyComponent() {
const { mutate, error } = api.post.create.useMutation();

return (
<form onSubmit={(e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
mutate({ title: formData.get('title') });
}}>
<input name="title" />
{error?.data?.zodError?.fieldErrors.title && (
{/** `mutate` returned with an error on the `title` */}
<span className="mb-8 text-red-500">
{error.data.zodError.fieldErrors.title}
</span>
)}

...
</form>
);
}
```

## Files

tRPC requires quite a lot of boilerplate that `create-t3-app` sets up for you. Let's go over the files that are generated:
Expand Down

1 comment on commit 10a5e0b

@vercel
Copy link

@vercel vercel bot commented on 10a5e0b Mar 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.