Skip to content

Author a schema for your metadata standard

Your metadata standard already exists, even if it only lives in a style guide or in people’s heads: “every page needs a type,” “timestamp should be an ISO date,” “API pages must declare a version.” A JSON Schema turns those agreements into a file that docmeta can enforce on every commit. This page walks through writing that schema — from a minimal one-field example up to the fuller built-in OKF schema — and shows how a document points itself at the schema you wrote.

docmeta validates against standard JSON Schema. If you can write JSON, you can write a docmeta schema — there is no docmeta-specific schema format to learn. A schema describes the frontmatter object: which keys are required, what type each value should be, and whether unexpected keys are allowed.

Here is the smallest schema worth shipping. It says: the metadata must be an object, it must have a title, and that title must be a non-empty string.

title.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["title"],
"properties": {
"title": { "type": "string", "minLength": 1 }
}
}

This is the extra.schema.json fixture that docmeta’s own test suite runs, so you can trust it compiles and validates exactly as shown. Four things are doing the work:

  • "$schema" at the top names the JSON Schema dialect this file is written in — here, draft 2020-12. docmeta reads this line to pick the right validation engine. (This is the schema’s own dialect declaration. It is different from the $schema key you put in a document to choose a schema — covered below.)
  • "type": "object" says the metadata is a set of key/value pairs, which frontmatter always is.
  • "required": ["title"] is the list of fields that must be present. A document missing title fails validation.
  • "properties" describes individual fields. title must be a string, and minLength: 1 rejects an empty title: that a contributor left blank.

A document with this frontmatter passes:

---
title: Getting started
---

A document with empty or missing title fails. That is the whole loop: declare the shape, and docmeta holds every file to it.

The single most important decision in a metadata schema is which fields are required and which are merely recommended. JSON Schema models this with one list:

  • Required — list the field in "required". Its absence is a hard failure.
  • Recommended (optional) — describe the field in "properties" but leave it out of "required". If the field is present, docmeta checks its type and format; if it’s absent, that’s fine.

This distinction is what lets you encode a standard that has a strict core and a softer edge. In the example below, type is mandatory, while title and tags are validated when present but never demanded:

metadata.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["type"],
"properties": {
"type": { "type": "string", "minLength": 1 },
"title": { "type": "string" },
"tags": {
"type": "array",
"items": { "type": "string" }
}
}
}

Requiring a field guarantees it exists; constraining it guarantees it’s usable. JSON Schema gives you the building blocks, and docmeta supports the standard format checks through ajv-formats. Two formats come up constantly in document metadata:

Use "format": "date-time" to require an ISO 8601 timestamp. This catches the classic fat-fingered date — 2026-13-01, June 5th, or a bare 2026-06-25 where a full timestamp was expected.

"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 datetime of last meaningful change."
}

Passes: 2026-06-25T14:30:00Z Fails: June 5th, 2026-13-01

Beyond formats, the everyday constraints carry most of the weight:

  • enum — restrict a field to a fixed set of values, ideal for a controlled type or status: "enum": ["guide", "reference", "tutorial"].
  • minLength — reject empty strings ("minLength": 1).
  • array + items — validate every element of a list, as with the tags example above.

By default, JSON Schema lets a document carry fields your schema never mentions. Whether to permit that is a deliberate choice, controlled by additionalProperties:

  • "additionalProperties": true (or omitting it) — extra keys are tolerated. Contributors can add experimental or tool-specific frontmatter without tripping the check. This is the lenient default and a good fit for a young or shared standard.
  • "additionalProperties": false — every key must be declared in "properties". An unexpected key is a failure. Use this to catch typos like titel: or to keep frontmatter tightly governed.

docmeta ships with one built-in schema, Open Knowledge Format (OKF) v0.1, addressed as google:okf:0.1. It’s a useful reference because it combines every technique on this page in a realistic standard. Reading it is the fastest way to see how the pieces fit together — and it’s the schema docmeta falls back to when no other schema applies.

src/schemas/okf/0.1.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "google:okf:0.1",
"title": "Open Knowledge Format (OKF) v0.1",
"description": "Frontmatter schema for OKF v0.1 concept files. OKF requires only `type`; all other fields are recommended, and unknown keys are explicitly tolerated.",
"type": "object",
"required": ["type"],
"additionalProperties": true,
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "A short string identifying the kind of concept."
},
"title": {
"type": "string",
"description": "Human-readable display name."
},
"description": {
"type": "string",
"description": "Single-sentence summary used for indexing and previews."
},
"resource": {
"type": "string",
"format": "uri",
"description": "A URI uniquely identifying the underlying asset."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Short strings for categorization."
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 datetime of last meaningful change."
}
}
}

Notice the design choices OKF makes, each one a decision you’ll face in your own schema:

  • Exactly one required field (type). OKF demands the bare minimum, so adopting it rarely breaks existing files.
  • additionalProperties: true, stated explicitly. Unknown keys are welcome by design — OKF is meant to be extended.
  • Recommended fields with real constraints. title, description, and tags are optional, but if present they’re checked. resource and timestamp carry uri and date-time formats.
  • A $id and human-readable title/description. These aren’t required for validation, but they document the schema for the humans who read it — and $id is what gives the built-in its google:okf:0.1 address. Setting a $id in a schema file of your own is documentation only; it does not register a new built-in id (built-ins are bundled into docmeta). You’d reference your own schema by its file path or URL, not by its $id.

A schema only matters once documents are validated against it. The most direct way to connect the two is to let a document name its own schema with a $schema key in its frontmatter. docmeta reads that key and validates the file against whatever it points to.

Here is a document naming the built-in OKF schema — the schema-ref.md fixture:

schema-ref.md
---
$schema: google:okf:0.1
type: guide
title: Self-Describing Document
---
# Self-Describing Document
This file names its own schema via `$schema`.

The $schema value can be a built-in id (as here), a path to a .json file you wrote, or a URL. To point at a local schema file instead:

---
$schema: ./schemas/metadata.schema.json
type: guide
title: My page
---
  1. Write your schema as a .json file using the patterns above, keeping the "$schema" dialect line at the top.

  2. Add $schema: to a document’s frontmatter, pointing at your file (or a built-in id, or a URL).

  3. Validate it:

    Terminal window
    npx docmeta validate path/to/document.md

    docmeta resolves the document’s $schema, validates the frontmatter, and reports any missing or malformed fields.