Back to Blog
ValidationZodTypeScriptData Quality

Data Validation: Ensuring Data Integrity

Implement robust data validation. Learn schema validation, sanitization, and patterns for maintaining data quality.

B
Bootspring Team
Engineering
February 26, 2026
2 min read

Data validation prevents bugs and security issues. This guide covers validation strategies using Zod and other tools.

Zod Schema Validation#

Basic Schemas#

1import { z } from 'zod'; 2 3const UserSchema = z.object({ 4 id: z.string().uuid(), 5 email: z.string().email(), 6 name: z.string().min(2).max(100), 7 age: z.number().int().min(0).max(150).optional(), 8 role: z.enum(['user', 'admin', 'moderator']), 9 createdAt: z.date(), 10}); 11 12type User = z.infer<typeof UserSchema>; 13 14// Validation 15const result = UserSchema.safeParse(input); 16if (!result.success) { 17 console.error(result.error.issues); 18}

Custom Validators#

1const PasswordSchema = z.string() 2 .min(8) 3 .max(100) 4 .refine( 5 (password) => /[A-Z]/.test(password), 6 { message: 'Password must contain uppercase letter' } 7 ) 8 .refine( 9 (password) => /[0-9]/.test(password), 10 { message: 'Password must contain number' } 11 ) 12 .refine( 13 (password) => /[!@#$%^&*]/.test(password), 14 { message: 'Password must contain special character' } 15 ); 16 17const SlugSchema = z.string() 18 .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Invalid slug format') 19 .max(100);

Nested Objects#

1const AddressSchema = z.object({ 2 street: z.string(), 3 city: z.string(), 4 state: z.string().length(2), 5 zipCode: z.string().regex(/^\d{5}(-\d{4})?$/), 6 country: z.string().default('US'), 7}); 8 9const OrderSchema = z.object({ 10 items: z.array(z.object({ 11 productId: z.string().uuid(), 12 quantity: z.number().int().positive(), 13 price: z.number().positive(), 14 })).min(1), 15 shippingAddress: AddressSchema, 16 billingAddress: AddressSchema.optional(), 17 total: z.number().positive(), 18});

Transformations#

1const InputSchema = z.object({ 2 email: z.string().email().toLowerCase(), 3 name: z.string().trim(), 4 tags: z.string().transform(s => s.split(',').map(t => t.trim())), 5 date: z.string().transform(s => new Date(s)), 6}); 7 8// Coercion for form data 9const FormSchema = z.object({ 10 age: z.coerce.number().int().positive(), 11 active: z.coerce.boolean(), 12 date: z.coerce.date(), 13});

Express Middleware#

1function validate<T extends z.ZodSchema>(schema: T) { 2 return (req: Request, res: Response, next: NextFunction) => { 3 const result = schema.safeParse({ 4 body: req.body, 5 query: req.query, 6 params: req.params, 7 }); 8 9 if (!result.success) { 10 return res.status(400).json({ 11 error: 'Validation failed', 12 details: result.error.issues.map(issue => ({ 13 path: issue.path.join('.'), 14 message: issue.message, 15 })), 16 }); 17 } 18 19 req.validated = result.data; 20 next(); 21 }; 22} 23 24// Usage 25const CreateUserSchema = z.object({ 26 body: UserSchema.omit({ id: true, createdAt: true }), 27}); 28 29app.post('/users', validate(CreateUserSchema), async (req, res) => { 30 const user = await createUser(req.validated.body); 31 res.json(user); 32});

Sanitization#

1import DOMPurify from 'isomorphic-dompurify'; 2 3const ContentSchema = z.object({ 4 title: z.string() 5 .trim() 6 .min(1) 7 .max(200) 8 .transform(s => DOMPurify.sanitize(s)), 9 10 html: z.string() 11 .transform(s => DOMPurify.sanitize(s, { 12 ALLOWED_TAGS: ['p', 'b', 'i', 'a', 'ul', 'li'], 13 ALLOWED_ATTR: ['href'], 14 })), 15});

Database Validation#

1// Prisma with Zod 2import { Prisma } from '@prisma/client'; 3 4const createUser = async (input: unknown) => { 5 const validated = UserSchema.parse(input); 6 7 return prisma.user.create({ 8 data: validated, 9 }); 10};

Validate at API boundaries, use type-safe schemas, and sanitize user-generated content.

Share this article

Help spread the word about Bootspring