Created
February 3, 2023 18:17
-
-
Save simonrelet/e76cda40e755ef1200166ba3fccf276a to your computer and use it in GitHub Desktop.
URL query validation and typing with NextJs and Zod
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Shared URL query that can be imported from anywhere. | |
const PaginationUrlQuerySchema = z.object({ | |
// Use .catch() in order to return a default value when validation fails. | |
// See https://zod.dev/?id=catch | |
page: z.coerce.number().min(0).catch(0), | |
}); | |
// Page specific URL query combined with shared ones. | |
const MyPageUrlQuerySchema = PaginationUrlQuerySchema.extend({ | |
enum: z.enum(["foo", "bar"]).catch("foo"), | |
}); | |
// Export the type so other pages can safely type links to this page (see | |
// `<Link />` usages bellow). | |
// Use `Partial` because URL query **should be optional**. | |
export type MyPageUrlQuery = Partial<z.infer<typeof MyPageUrlQuerySchema>>; | |
export function getServerSideProps(ctx: GetServerSidePropsContext) { | |
// `ctx.query` is always defined so this will never throw a validation error. | |
const query = MyPageUrlQuerySchema.parse(ctx.query); | |
} | |
export default function Page() { | |
const router = useRouter(); | |
// Can be wrapped in a `useMemo` if there are performances issues. | |
const query = MyPageUrlQuerySchema.parse(router.query); | |
return ( | |
<main> | |
{query.page > 0 ? ( | |
<Link | |
href={{ | |
// `satisfies` allows us to type check the object. | |
query: { ...query, page: query.page - 1 } satisfies MyPageUrlQuery, | |
}} | |
> | |
Previous page | |
</Link> | |
) : null} | |
<Link | |
href={{ | |
query: { ...query, page: query.page + 1 } satisfies MyPageUrlQuery, | |
}} | |
> | |
Next page | |
</Link> | |
<Link | |
href={{ | |
query: { ...query, enum: "bar" } satisfies MyPageUrlQuery, | |
}} | |
> | |
Only bar | |
</Link> | |
</main> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment