Schema Evolution
Keep old MongoDB documents readable while your current Zod schemas stay clean.
Zog keeps migration concerns outside the current schema. Use this page when the stored shape has history, but application code should only see the current model.
Collection Names
If the logical model name differs from the physical Mongo collection, pass
collectionName:
const userModel = createModel("users", userSchema, {
primaryKey: "id",
collectionName: "user_accounts",
});If a collection used to have another name, declare it as legacy:
const userModel = createModel("users", userSchema, {
primaryKey: "id",
collectionName: "user_accounts",
legacyCollectionNames: ["users"],
});If user_accounts does not exist yet but users does, repository and index
operations use the declared legacy collection. If both names exist, Zog throws
instead of choosing between split data.
Collection Name Policy
Use collectionNamePolicy when you want a naming rule for every model:
const db = defineDb([userModel] as const, {
mongoClient,
databaseName: "app",
collectionNamePolicy: "snake",
collectionNameCompatibility: "error",
});collectionNameCompatibility: "error" adds a guard before repository and index
operations, so an undeclared old collection is reported before Zog creates a new
one.
Key Renames
For stored documents that still use old key names, declare read-time key renames:
const userModel = createModel("users", userSchema, {
primaryKey: "id",
legacyKeyRenames: [
{ from: "full_name", to: "name" },
{ from: "profile.display_name", to: "profile.displayName" },
{ from: "teams[].members[].full_name", to: "teams[].members[].name" },
],
});legacyKeyRenames runs before schema parsing on reads. [] applies the rename
to every object in an array. If both the legacy and current keys exist, the
current key wins. Zog removes the legacy key from the parse candidate, but does
not rewrite MongoDB automatically.
Custom Legacy Shapes
Use normalizeLegacy when the old shape needs custom code:
const userModel = createModel("users", userSchema, {
primaryKey: "id",
normalizeLegacy: (doc) => ({
...doc,
name: doc.name ?? [doc.firstName, doc.lastName].filter(Boolean).join(" "),
}),
});Declarative key renames run before normalizeLegacy, so the custom function can
focus on the cases that are not simple renames.