Validation made easy with Zod: How to ensure accuracy and quality in forms
Tags: zod, vue, typescript
Published: 2023-01-22 18:11:31

Data validation is a crucial part of any web application. It ensures that the data provided by the user is accurate and valid before being sent to the server. However, data validation can be a tedious and laborious task, especially when it comes to complex forms.

But what if I told you there's a way to make this task easier and more efficient? The Zod library is the perfect solution for this issue. It provides a simple and concise syntax for defining validation rules and makes it easy to detect input data errors.

When combined with Vue.js, Zod becomes even more powerful. In this article, we will show you how to use Zod to create a precise and detailed address validation schema and how to apply it in a Vue.js component using Vue 3 composition. Additionally, we will see how to use regular expressions to validate the zip code and v-mask to apply masks.

To get started, we will install Zod in our Vue.js project:

```
npm install zod
```

Next, we will create the validation schema for the address:

```
import { object, string, number } from 'zod';

const addressSchema = object({
  street: string().min(3).max(50).required(),
  number: number().required(),
  city: string().min(3).max(50).required(),
  state: string().min(2).max(2).required(),
  zip: string().pattern(/^\d{5}-\d{3}$/).required()
});
```

With this schema, we define that the street must be a string with at least 3 characters and at most 50 and is required. The number must be a number and is required. The city must be a string with at least 3 characters and at most 50, and is required. The state must be a string with at least 2 characters and at most 2, and is required. And the zip code must be a string in the format "12345-678" (considering Brazilian zip code) and is required.

Now, we will create our Vue.js component using composition:

```
<template>
  <form>
    <label for="street">Street:</label>
    <input id="street" v-model="address.street" type="text" />
    <br />
    <label for="number">Number:</label>
    <input id="number" v-model="address.number" type="number" />
    <br />
    <label for="city">City:</label>
    <input id="city" v-model="address.city" type="text" />
    <br />
    <label for="state">State:</label>
    <input id="state" v-model="address.state" type="text" minlength="2" maxlength="2" />
    <br />
    <label for="zip">Zip:</label>
    <input id="zip" v-model="address.zip" v-mask="'#####-###'" type="text"  placeholder="Zip Code" />
    <br />
    <button @click.prevent="submitForm">Submit</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
import { object, string, number } from 'zod'

// Ideally, put this code in a schema file
const addressSchema = object({
  street: string().min(3).max(50).required(),
  number: number().required(),
  city: string().min(3).max(50).required(),
  state: string().min(2).max(2).required(),
  zip: string().pattern(/^\d{5}-\d{3}$/).required()
})

const address = ref({
  street: '',
  number: 0,
  city: '',
  state: '',
  zip: ''
})

const submitForm = async () => {
  try {
    const valid = addressSchema.validate(address.value)

    if (!valid) return

    // Send data to server
  } catch (err) {
    console.log(err.errors)
  }
}
</script>
```

Here, we have created a simple form with the address fields and added v-model to each field to bind them to the properties of the address object. We also added v-mask to apply a mask on the zip code. When the user clicks the "Submit" button, the form is submitted and the Zod validation schema is used to validate the data. If the data is valid, it is sent to the server. Otherwise, the validation errors are displayed in the console.

In summary, Zod is an amazing tool for data validation when used in conjunction with Vue.js. It allows creating precise and easy-to-use validation schemas to ensure that input data is valid. With Zod, it is possible to ensure the quality of input data in your Vue.js application and avoid errors.