Skip to content

Instantly share code, notes, and snippets.

@simonrelet
Created February 3, 2023 18:17
Show Gist options
  • Save simonrelet/e76cda40e755ef1200166ba3fccf276a to your computer and use it in GitHub Desktop.
Save simonrelet/e76cda40e755ef1200166ba3fccf276a to your computer and use it in GitHub Desktop.
URL query validation and typing with NextJs and Zod
// 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