Zod and Query String Variables in Nuxt

We all know how important it is to validate the payloads of POST requests to our API endpoints and Zod makes this super easy to do! BUT did you know Zod is also super useful for working with data from the user’s query string variables?

Let me show you how to do this with your Nuxt apps!

How To Use Zod with Query Variables

Using zod to validate and get valid data from a query string in Nuxt is straightforward. Here is an example:

<script setup lang="ts">
import { z } from 'zod';
const route = useRoute();

// Define the expected shape of the query string data
const schema = z.object({
  q: z.string().optional(),
  page: z.coerce.number().optional().default(1),

// create a computed property with the zod parse method
// and the route.query object from Router
const validData = computed(() => {
  try {
    return schema.parse(route.query);
  } catch (e) {
    alert('invalid query string!');
    return null;

//  Now validData is guaranteed to be what you expect!

So, what are the benefits here?

Get Predictable Valid Data

First, I can rest assured the query string variables look like I’d expect them to. Check out these examples:

  • ?q=hello&q=world - errors because q is an array instead of a string
  • ?page=hello - errors because page is not a number
  • ?q=hello - The resulting data is{ q: 'hello', page: 1 } because q is a valid string and page is a default of 1
  • ?page=1 - The resulting data is { page: 1 } because page is a valid number (q isn’t provided but that’s ok, it’s marked optional)
  • ?page=2&q=hello - { q: "hello", page: 2 } - I think you get the picture :)

Ignore Useless Data

You know what query variables you expect, don’t clutter your validData with random query variables the user might insert into the query string. Using zod’s parse function eliminates any keys from the resulting data that aren’t defined in the schema.

// ?q=hello&page=1&extra=12  
  "q": "hello",
  "page": 1
  // "extra" property does not exist!

Coerce Query String Data

One of the most useful features of this strategy is that I never have to manually coerce data again. What do I mean? Query string values are ALWAYS strings (or arrays of strings). In times past, that meant calling parseInt whenever working with a number from the query string.

No more! Simply mark the variable with the coerce keyword in your schema, and zod does the conversion for you.

const schema = z.object({
  //        right here
  page: z.coerce.number().optional(),

Default Values

Rely on a complete query variable object and stop checking whether or not values exist in the query string by providing defaults.

const schema = z.object({
    // ...
  page: z.coerce.number().optional().default(1), //  default!

Practical Use Case

This is useful anywhere but I’ve found using this strategy especially helpful when dealing with all the ways you can paginate, sort, and filter data in a table. Easily store your states (like page, perPage, search query, sort by columns, etc in the query string and make your exact view of the table with particular datasets shareable via the URL).


In conclusion, this strategy for dealing with query strings pairs perfectly with any Nuxt application. Next time you accept data via the query string, consider using zod for a DX.

If you’d like live demo of this strategy, check out the following playground on StackBlitz.

Original Article written by Daniel Kelly.