Essentials

Schemas

Define your MongoDB document schemas with Zod for type-safe validation.

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>;
Always include an _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
We highly recommend using 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 TypeMongoDB 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:

  1. Validation — on insert and update operations, the payload is validated against the schema
  2. 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).

Copyright © 2026