Parsing URL search params with Zod
zod
is a library to validate if an arbitrary
user-generated data conforms with a specific type at runtime. In this post, I
want to talk about using it to validate URL search params.
Say we want to put our application state in the URL. This is certainly nice but it requires us to deal with using the search param in a type-safe way — since the user can type there pretty much anything, you need to make sure the search param value can be parsed as a number.
This pattern revolves around using .catch
to
provide a “catch-all” value in case of a parsing error, which is exactly what
we want: to provide a default a safe-to-use value in case we can’t work with
the user’s input.
Let’s build an example in which we need to parse the pageNumber
search param.
We expect it to be a number, but in case it isn’t, we’ll fall back to 0
. We
can do this with the schema z.number().catch(0)
.
Here’s a live example — try to break the app by passing values that cannot be parsed as numbers:
import z from 'zod' import * as React from 'react' export default function App() { const searchParams = new URLSearchParams(window.location.search) const pageNumberRaw = searchParams.get('pageNumber') const pageNumber = z.number().catch(0).parse(Number(pageNumberRaw)) return <pre>{JSON.stringify({ pageNumber, pageNumberRaw }, null, 2)}</pre> }
We don’t need to use a number as the fallback value — it’s fine to use null
,
undefined
, or any other really, we’d just need to update the schema
accordingly:
z.number().nullable().catch(null)z.number().optional().catch(undefined)
The nice thing about Zod is that it prevents us from using type-casting and other TypeScript patterns I’d prefer to avoid — I don’t feel like struggling with the type system and the code is more elegant.