- Published on
Using Zod to Create Schema with Existing Types
- Authors
- Name
- Ripal & Zalak
Using Zod to Create Schema with Existing Types
When working with APIs, it’s often necessary to validate incoming data against a predefined type. Zod, a powerful TypeScript schema declaration and validation library, allows you to achieve this with ease. However, what happens when you want to create a Zod schema based on an existing type, such as Axios' Method
? Let's dive into it!
Problem Statement
Suppose you have an endpoint that accepts a parameter method
that must conform to the Method
type from Axios:
export type Method =
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH'
| 'purge'
| 'PURGE'
| 'link'
| 'LINK'
| 'unlink'
| 'UNLINK'
You want to ensure that your schema validation matches this type exactly, avoiding redundant declarations or mismatches. How do you achieve this in Zod?
Solution
z.enum
with Explicit Values
Using The simplest approach is to recreate the Method
type as an array of string literals and use z.enum
:
import { z } from 'zod'
const MethodSchema = z.enum([
'get',
'GET',
'delete',
'DELETE',
'head',
'HEAD',
'options',
'OPTIONS',
'post',
'POST',
'put',
'PUT',
'patch',
'PATCH',
'purge',
'PURGE',
'link',
'LINK',
'unlink',
'UNLINK',
])
While effective, this approach requires manually synchronizing the values, which may lead to errors if the Method
type changes in Axios.
Inferring from Existing Types
A more dynamic and TypeScript-friendly approach is to infer the schema directly from the Method
type. You can achieve this by using Zod’s z.enum
combined with a runtime array:
import { z } from 'zod'
import type { Method } from 'axios'
const methods = [
'get',
'GET',
'delete',
'DELETE',
'head',
'HEAD',
'options',
'OPTIONS',
'post',
'POST',
'put',
'PUT',
'patch',
'PATCH',
'purge',
'PURGE',
'link',
'LINK',
'unlink',
'UNLINK',
] as const
const MethodSchema = z.enum(methods)
Here, the as const
assertion ensures that TypeScript infers the correct literal types, enabling Zod to validate against them.
z.custom
for Custom Types
Using Zod’s z.custom
method can validate against a custom TypeScript type:
import { z } from 'zod'
import type { Method } from 'axios'
const MethodSchema = z.custom<Method>((value) => {
return typeof value === 'string' && methods.includes(value as Method)
})
This approach validates against the Method
type while keeping the implementation in sync with the runtime values.
Complete Example
Here’s how you can use the MethodSchema
in a complete schema for validating API requests:
import { z } from 'zod'
const ApiRequestSchema = z.object({
method: MethodSchema,
url: z.string().url(),
headers: z.record(z.string()).optional(),
})
// Example usage
const requestData = {
method: 'GET',
url: 'https://api.example.com',
headers: {
Authorization: 'Bearer token',
},
}
try {
ApiRequestSchema.parse(requestData)
console.log('Validation successful!')
} catch (err) {
console.error('Validation failed:', err.errors)
}