Usage Examples
This page provides practical examples of using Zod validation in real-world scenarios.
API Request Validation
Express.js Middleware
Create reusable validation middleware for Express.js:
typescript
import { Request, Response, NextFunction } from 'express';
import { validateUserInput, validateServerConfig } from './validation.js';
// Generic validation middleware
function validateBody<T>(validator: (data: unknown) => { success: boolean; data?: T; error?: string }) {
return (req: Request, res: Response, next: NextFunction) => {
const result = validator(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error
});
}
// Attach validated data to request
req.validatedBody = result.data;
next();
};
}
// Use in routes
app.post('/api/users',
validateBody(validateUserInput),
(req, res) => {
const user = req.validatedBody; // Fully typed!
// Create user logic...
res.json({ success: true, user });
}
);
Fastify Integration
typescript
import Fastify from 'fastify';
import { UserInputSchema } from './validation.js';
const fastify = Fastify();
// Register schema for automatic validation
fastify.post('/users', {
schema: {
body: UserInputSchema
}
}, async (request, reply) => {
// request.body is automatically validated and typed
const user = request.body;
return { success: true, user };
});
Configuration Loading
Application Startup
typescript
import { ConfigManager } from './config.js';
import { createLoggerFromConfig } from './logger.js';
async function startApplication() {
// Load and validate configuration
const configManager = new ConfigManager();
const configResult = configManager.loadConfig();
if (!configResult.success) {
console.error('Failed to load configuration:', configResult.error);
process.exit(1);
}
const config = configResult.data;
// Create logger from validated config
const logger = createLoggerFromConfig(config.logging);
logger.info(`Starting ${config.app.name} v${config.app.version}`);
// Start server with validated config
const server = createServer(config.server);
server.listen(config.server.port, config.server.host, () => {
logger.info(`Server running on ${config.server.host}:${config.server.port}`);
});
}
startApplication().catch(console.error);
Environment-Specific Configuration
typescript
import { validateAppConfig } from './validation.js';
function loadEnvironmentConfig() {
const env = process.env.NODE_ENV || 'development';
const configPath = `config.${env}.json`;
try {
const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
const result = validateAppConfig(configData);
if (!result.success) {
throw new Error(`Invalid ${env} configuration: ${result.error}`);
}
return result.data;
} catch (error) {
console.error(`Failed to load ${env} configuration:`, error.message);
throw error;
}
}
Form Validation
React Hook Form Integration
typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { UserInputSchema } from './validation.js';
function UserForm() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
resolver: zodResolver(UserInputSchema)
});
const onSubmit = (data) => {
// data is automatically validated and typed
console.log('Valid user data:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" />
{errors.name && <span>{errors.name.message}</span>}
<input {...register('email')} placeholder="Email" />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('age', { valueAsNumber: true })} placeholder="Age" />
{errors.age && <span>{errors.age.message}</span>}
<button type="submit">Submit</button>
</form>
);
}
Vue.js Composition API
typescript
import { ref, computed } from 'vue';
import { validateUserInput } from './validation.js';
export function useUserValidation() {
const formData = ref({
name: '',
email: '',
age: undefined
});
const validationResult = computed(() => {
return validateUserInput(formData.value);
});
const isValid = computed(() => validationResult.value.success);
const errors = computed(() =>
validationResult.value.success ? null : validationResult.value.error
);
const submitForm = () => {
if (isValid.value) {
const validatedData = validationResult.value.data;
// Submit logic...
}
};
return {
formData,
isValid,
errors,
submitForm
};
}
Database Operations
Prisma Integration
typescript
import { PrismaClient } from '@prisma/client';
import { validateUserInput } from './validation.js';
const prisma = new PrismaClient();
async function createUser(userData: unknown) {
// Validate input before database operation
const result = validateUserInput(userData);
if (!result.success) {
throw new Error(`Invalid user data: ${result.error}`);
}
const validatedUser = result.data;
// Create user with validated data
const user = await prisma.user.create({
data: {
name: validatedUser.name,
email: validatedUser.email,
age: validatedUser.age,
preferences: validatedUser.preferences
}
});
return user;
}
MongoDB Integration
typescript
import { MongoClient } from 'mongodb';
import { validateUserInput } from './validation.js';
async function insertUser(db: Db, userData: unknown) {
const result = validateUserInput(userData);
if (!result.success) {
throw new Error(`Validation failed: ${result.error}`);
}
const collection = db.collection('users');
const insertResult = await collection.insertOne({
...result.data,
createdAt: new Date(),
updatedAt: new Date()
});
return insertResult;
}
File Processing
JSON File Validation
typescript
import { safeParseAndValidate, AppConfigSchema } from './validation.js';
async function processConfigFile(filePath: string) {
try {
const fileContent = await fs.readFile(filePath, 'utf-8');
const result = safeParseAndValidate(fileContent, AppConfigSchema);
if (!result.success) {
console.error(`Invalid config file ${filePath}:`, result.error);
return null;
}
console.log(`Loaded valid config from ${filePath}`);
return result.data;
} catch (error) {
console.error(`Failed to read config file ${filePath}:`, error.message);
return null;
}
}
CSV Data Validation
typescript
import csv from 'csv-parser';
import { validateUserInput } from './validation.js';
function processUserCSV(filePath: string) {
const validUsers = [];
const errors = [];
return new Promise((resolve, reject) => {
fs.createReadStream(filePath)
.pipe(csv())
.on('data', (row) => {
const result = validateUserInput(row);
if (result.success) {
validUsers.push(result.data);
} else {
errors.push({
row,
error: result.error
});
}
})
.on('end', () => {
resolve({
validUsers,
errors,
total: validUsers.length + errors.length
});
})
.on('error', reject);
});
}
Testing Validation
Unit Tests
typescript
import { describe, it, expect } from '@jest/globals';
import { validateUserInput } from './validation.js';
describe('User Input Validation', () => {
it('should validate correct user input', () => {
const validUser = {
name: 'John Doe',
email: 'john@example.com',
age: 30
};
const result = validateUserInput(validUser);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.name).toBe('John Doe');
expect(result.data.email).toBe('john@example.com');
expect(result.data.age).toBe(30);
}
});
it('should reject invalid email', () => {
const invalidUser = {
name: 'John Doe',
email: 'invalid-email',
age: 30
};
const result = validateUserInput(invalidUser);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error).toContain('Invalid email format');
}
});
});
Integration Tests
typescript
import request from 'supertest';
import { app } from './app.js';
describe('User API', () => {
it('should create user with valid data', async () => {
const validUser = {
name: 'Jane Doe',
email: 'jane@example.com',
age: 25
};
const response = await request(app)
.post('/api/users')
.send(validUser)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.user.name).toBe('Jane Doe');
});
it('should reject invalid user data', async () => {
const invalidUser = {
name: '',
email: 'invalid-email'
};
const response = await request(app)
.post('/api/users')
.send(invalidUser)
.expect(400);
expect(response.body.error).toBe('Validation failed');
expect(response.body.details).toContain('Name is required');
});
});
Performance Considerations
Schema Caching
typescript
// Cache compiled schemas for better performance
const schemaCache = new Map();
function getCachedValidator(schemaName: string, schema: ZodSchema) {
if (!schemaCache.has(schemaName)) {
schemaCache.set(schemaName, schema.parse.bind(schema));
}
return schemaCache.get(schemaName);
}
// Use cached validator
const validateUser = getCachedValidator('user', UserInputSchema);
Async Validation
typescript
// For large datasets, use async validation
async function validateLargeDataset(data: unknown[]) {
const results = await Promise.allSettled(
data.map(item => Promise.resolve(validateUserInput(item)))
);
const valid = [];
const invalid = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled' && result.value.success) {
valid.push(result.value.data);
} else {
invalid.push({ index, data: data[index], error: result.reason });
}
});
return { valid, invalid };
}