Schemas
Mongalayer uses Zod schemas to validate documents on insert and update operations, and to derive TypeScript types used throughout the system.
Defining a schema
Each collection in your Mongalayer configuration requires a Zod object schema that describes the shape of documents in that collection:
import z from "zod";
export const userSchema = z.strictObject({
_id: z.string(),
name: z.string(),
email: z.string(),
roles: z.array(z.enum(["admin", "user"])),
createdAt: z.date(),
settings: z.strictObject({
theme: z.enum(["light", "dark"])
})
});
export type User = z.infer<typeof userSchema>;
_id field in your schema. MongoDB requires it and Mongalayer uses it internally for access resolution.Strict vs. non-strict schemas
Both z.strictObject() and z.object() work for validation:
z.strictObject()— rejects any properties not defined in the schema (recommended)z.object()— allows additional properties not defined in the schema
z.strictObject() to prevent accidental insertion of unintended fields and to ensure your access control rules work as expected. Data that comes from a client needs to be treated as untrusted, and strict schemas provide an extra layer of safety by rejecting any fields that aren't explicitly defined.// Strict: will reject documents with extra fields
export const projectSchema = z.strictObject({
_id: z.string(),
name: z.string(),
description: z.string().optional(),
owner: z.string(),
createdAt: z.date(),
updatedAt: z.date().nullable()
});
Supported types
Mongalayer supports all Zod types that are compatible with MongoDB documents:
| Zod Type | MongoDB Equivalent |
|---|---|
z.string() | String |
z.number() | Number (int/double) |
z.boolean() | Boolean |
z.date() | Date |
z.null() | Null |
z.array() | Array |
z.object() | Embedded document |
z.enum() | String (constrained) |
z.tuple() | Array (fixed length) |
z.union() | Mixed types |
Optional and nullable fields
z.strictObject({
description: z.string().optional(), // Field may be missing
updatedAt: z.date().nullable(), // Field can be null
notes: z.string().optional().nullable() // Can be missing or null
})
Nested objects
z.strictObject({
_id: z.string(),
tags: z.array(z.strictObject({
type: z.enum(["a", "b", "c"]),
value: z.string()
})),
data: z.strictObject({
location: z.strictObject({
coordinates: z.tuple([z.number(), z.number()]),
city: z.string()
}).optional()
})
})
Schema and access
Schemas are used in two ways:
- Validation — on insert and update operations, the payload is validated against the schema
- Access control — field-level permissions reference the schema's root-level keys
When using $set or $unset in updates, Mongalayer validates the update values against the relevant parts of your schema. For $unset, it verifies that the field is marked as optional() in the schema before allowing removal.
Registering schemas
Pass your schemas when creating the Mongalayer instance:
import { Mongalayer } from "@mongalayer/server";
const layer = new Mongalayer(mongoClient, {
users: {
schema: userSchema,
access: userAccess
},
projects: {
schema: projectSchema,
access: projectAccess
}
});
If a collection is not registered in the configuration, Mongalayer will still execute operations on it but without schema validation or access control (public access).