Schemas and validation
Start with any JSON, then add a JSON Schema so bad data is rejected. Versions are kept, and activation checks your existing records first.
A new collection takes any JSON. That is deliberate: when you are still figuring out what you are building, a schema is in the way.
Once the shape settles, add a schema and the API starts rejecting anything that does not match — a typo in a field name, a number sent as text, a missing title.
Add one from the dashboard
Open the collection, go to the Schema tab, and add fields with the builder: name, type, required or not. Or switch to the raw editor and paste JSON Schema directly.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"properties": {
"title": { "type": "string", "minLength": 1 },
"done": { "type": "boolean" },
"priority": { "type": "string", "enum": ["low", "medium", "high"] },
"tags": { "type": "array", "items": { "type": "string" } },
"due": { "type": ["string", "null"], "format": "date" }
},
"required": ["title", "done"]
}additionalProperties: false is the strict choice: anything you did not list is
rejected. Leave it out while you are still adding fields.
Add one from the API
curl -s -X POST "$BAAS_URL/workspaces/$BAAS_WS/collections/todos/schemas" \
-H "Authorization: Bearer $BAAS_KEY" -H "Content-Type: application/json" \
-d '{"schema": { … }, "migrationNotes": "require a title"}'Needs schemas:write.
What happens to records that break the new rules
This is the part people worry about, and the answer is: nothing, unless you say so.
Before a version is switched on, every existing record is checked. If any would become
invalid, activation is refused with 422 SCHEMA_MIGRATION_BLOCKED and a report of
what failed, so you can fix the data first.
Check before you commit to it:
curl -s -X POST "$BAAS_URL/workspaces/$BAAS_WS/collections/todos/schemas/validate" \
-H "Authorization: Bearer $BAAS_KEY" -H "Content-Type: application/json" \
-d '{"schema": { … }}'{ "data": { "recordsChecked": 120, "validRecords": 118, "invalidRecords": 2,
"samples": [ { "recordId": "01a1…", "issues": [ … ] } ] } }If you genuinely want to go ahead anyway, pass "allowInvalidRecords": true.
Activating a schema does not rewrite your old records. Each keeps the
schemaVersion it was last checked against, and is validated against the current schema
the next time it is written.
Versions
Schema history is append-only: versions are added, never edited or deleted. Each records who made it, when, and your migration note.
# stage a version without switching it on
-d '{"schema": { … }, "activate": false}'
# switch one on later, or roll back to an older one
curl -s -X POST "…/schemas/2/activate" -H "Authorization: Bearer $BAAS_KEY"
curl -s -X POST "…/schemas/1/activate" -H "Authorization: Bearer $BAAS_KEY"
# go back to accepting anything (history is kept)
curl -s -X POST "…/schemas/deactivate" -H "Authorization: Bearer $BAAS_KEY"When a write is rejected
{
"error": {
"code": "SCHEMA_VALIDATION_FAILED",
"message": "Record does not match collection schema",
"details": {
"issues": [
{ "path": "/email", "message": "must match format \"email\"", "keyword": "format" },
{ "path": "/title", "message": "is required", "keyword": "required" }
],
"schemaVersion": 3
}
}
}path is a pointer into your data, so /email means the email field. These map
cleanly onto form fields if you are showing errors to a user.
What JSON Schema features work
Supported: the usual types, nested objects, arrays (including arrays of objects), enums,
const, nullable types like ["string","null"], required and optional fields, number
and string constraints, pattern, format, the combinators allOf / anyOf / oneOf
/ not / if / then / else, and local $defs with $ref. A schema can also
describe a non-object, such as an array of numbers.
A schema is checked before it is accepted, and refused if it would not do what you expect:
- it must be valid JSON Schema draft 2020-12;
- unknown keywords are rejected, so
requirdis an error rather than a rule that silently does nothing; - only local
$refs — nothing is fetched from the internet; - regular expressions are length-limited and screened for catastrophic backtracking, because they run on every write;
- only known
formatvalues, so a typo cannot disable a check; - at most 64 KB and 32 levels deep.
Anything else comes back as 400 INVALID_SCHEMA with the specific problem.
Hints for the dashboard's form editor
x-ui keywords are ignored by validation but make the generated form nicer:
{ "description": { "type": "string", "x-ui": { "label": "Notes", "multiline": true } } }Next steps
View this page as Markdown — handy for copying into an editor or an AI assistant.