2.9 KiB
2.9 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Choose strict() vs strip() for Unknown Keys | MEDIUM-HIGH | Default passthrough mode leaks unexpected data; strict() catches schema mismatches, strip() silently removes extras | object, strict, strip, passthrough |
Choose strict() vs strip() for Unknown Keys
By default, Zod objects use .strip() behavior, silently removing unrecognized keys. This can hide schema/data mismatches. Use .strict() to reject unknown keys (catching errors) or explicitly use .strip() to document the intention.
Default behavior (strip - silent removal):
import { z } from 'zod'
const userSchema = z.object({
id: z.string(),
name: z.string(),
})
const input = {
id: '123',
name: 'John',
role: 'admin', // Extra field
secretToken: 'abc123', // Another extra field
}
const user = userSchema.parse(input)
// { id: '123', name: 'John' }
// Extra fields silently removed - was this intentional?
Using strict() to catch schema mismatches:
import { z } from 'zod'
const userSchema = z.object({
id: z.string(),
name: z.string(),
}).strict()
const input = {
id: '123',
name: 'John',
role: 'admin',
}
userSchema.parse(input)
// ZodError: Unrecognized key(s) in object: 'role'
// This catches:
// - Client sending fields the server doesn't expect
// - Schema out of sync with actual data structure
// - Typos in field names
When to use each mode:
// strict() - Catch unexpected data (API contracts)
const apiRequestSchema = z.object({
action: z.string(),
payload: z.unknown(),
}).strict() // Fail if client sends unknown fields
// strip() - Clean up data (explicit intention)
const dbInsertSchema = z.object({
name: z.string(),
email: z.string(),
}).strip() // Explicitly remove metadata before insert
// passthrough() - Keep everything (pass-through proxy)
const proxySchema = z.object({
id: z.string(),
}).passthrough() // Keep fields we don't validate
const input = { id: '123', extra: 'data' }
proxySchema.parse(input) // { id: '123', extra: 'data' }
Choosing the right mode:
| Mode | Behavior | Use When |
|---|---|---|
.strict() |
Reject unknown keys | API contracts, security-sensitive, debugging |
.strip() (default) |
Remove unknown keys | General validation, data cleaning |
.passthrough() |
Keep unknown keys | Proxying, partial validation |
Handling specific unknown keys:
const schema = z.object({
id: z.string(),
name: z.string(),
}).catchall(z.unknown()) // Allow any additional fields of any type
// Or restrict additional fields to specific type
const metadataSchema = z.object({
id: z.string(),
}).catchall(z.string()) // Only allow string extras
When NOT to use this pattern:
.strict(): When forwarding data to another system that may add fields.passthrough(): When you need to ensure only known fields are stored
Reference: Zod API - Objects