Picking and Omitting Fields
pick()
pick() returns a new schema containing only the fields listed:
final userSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'password': z.string(),
'name': z.string(),
'role': z.string(),
});
final publicUserSchema = userSchema.pick(['id', 'email', 'name']);
// publicUserSchema validates only id, email, name
// password and role are not expected and not included in output
publicUserSchema.parse({
'id': '550e8400-e29b-41d4-a716-446655440000',
'email': 'alice@example.com',
'name': 'Alice',
});
Keys not present in the original schema are silently ignored. The original schema is not modified.
omit()
omit() returns a new schema with the listed fields removed:
final userSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'password': z.string(),
'name': z.string(),
'role': z.string(),
});
final publicUserSchema = userSchema.omit(['password']);
// publicUserSchema validates: id, email, name, role (no password)
publicUserSchema.parse({
'id': '550e8400-e29b-41d4-a716-446655440000',
'email': 'alice@example.com',
'name': 'Alice',
'role': 'user',
});
pick vs omit
Use pick when you want a small subset of fields. Use omit when you want most fields with a few excluded.
| Method | Use when |
|---|---|
pick | Keeping a few fields from a large schema |
omit | Excluding a few sensitive or server-generated fields |
Common patterns
Public vs private views
final userSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'password': z.string(),
'salt': z.string(),
'firstName': z.string(),
'lastName': z.string(),
'createdAt': z.dateTime(),
});
// Public API response: no credentials
final publicSchema = userSchema.omit(['password', 'salt']);
// Login request: credentials only
final loginSchema = userSchema.pick(['email', 'password']);
// Admin view: everything
final adminSchema = userSchema;
Form schemas
Server-generated fields (id, timestamps) are absent on creation and optionally absent on update:
final productSchema = z.object({
'id': z.string().uuid(),
'name': z.string(),
'price': z.double().positive(),
'stock': z.integer().nonNegative(),
'createdAt': z.dateTime(),
'updatedAt': z.dateTime(),
});
// Create form: no id or timestamps
final createProductSchema = productSchema.omit(['id', 'createdAt', 'updatedAt']);
// Update form: editable fields only
final updateProductSchema = productSchema.pick(['name', 'price', 'stock']);
// Full response
final productResponseSchema = productSchema;
API request/response
final postSchema = z.object({
'id': z.string().uuid(),
'title': z.string(),
'content': z.string(),
'authorId': z.string().uuid(),
'published': z.boolean(),
'createdAt': z.dateTime(),
'updatedAt': z.dateTime(),
});
// POST: create new post
final createPostSchema = postSchema.omit(['id', 'createdAt', 'updatedAt']);
// PATCH: update existing post
final updatePostSchema = postSchema.pick(['title', 'content', 'published']);
// GET: full post
final postResponseSchema = postSchema;
Combine with extend
Chain omit or pick with extend to produce derived schemas with additional fields:
final baseUserSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'password': z.string(),
'name': z.string(),
});
// Public schema with a computed display name field added
final publicUserSchema = baseUserSchema
.omit(['password'])
.extend({'displayName': z.string()});
// publicUserSchema has: id, email, name, displayName
Nested fields
pick and omit only operate on top-level keys. For nested fields, recreate the nested schema manually:
final userSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'profile': z.object({
'avatar': z.string().url(),
'bio': z.string(),
'website': z.string().url().optional(),
}),
});
// Pick top-level
final basicSchema = userSchema.pick(['id', 'email']);
// For a nested subset, compose manually
final userWithAvatarSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'profile': z.object({
'avatar': z.string().url(),
// bio and website excluded
}),
});
Real-world example: user management API
final userSchema = z.object({
'id': z.string().uuid(),
'email': z.string().email(),
'password': z.string(),
'salt': z.string(),
'firstName': z.string(),
'lastName': z.string(),
'role': z.string().oneOf(['admin', 'user', 'guest']),
'emailVerified': z.boolean(),
'createdAt': z.dateTime(),
'updatedAt': z.dateTime(),
'lastLogin': z.dateTime().optional(),
});
// POST /auth/register
final registerSchema = userSchema.pick(['email', 'password', 'firstName', 'lastName']);
// POST /auth/login
final loginSchema = userSchema.pick(['email', 'password']);
// GET /users/:id (public profile)
final publicProfileSchema = userSchema.pick(['id', 'firstName', 'lastName', 'createdAt']);
// GET /users/me
final currentUserSchema = userSchema.omit(['password', 'salt']);
// PATCH /users/:id
final updateProfileSchema = userSchema.pick(['firstName', 'lastName']);
// GET /admin/users/:id
final adminUserSchema = userSchema.omit(['password', 'salt']);
API reference
pick(keys)
ZemaObject<Map<String, dynamic>> pick(List<String> keys)
Returns a new schema containing only the specified fields. Keys absent from the original shape are silently ignored. The original schema is not modified.
omit(keys)
ZemaObject<Map<String, dynamic>> omit(List<String> keys)
Returns a new schema with the specified fields removed. Keys absent from the original shape are silently ignored. The original schema is not modified.
Next steps
- Merging and Extending: add or override fields with
extend()andmerge() - Discriminated Unions: validate different shapes based on a type field