Typed Documents
How a Hypermedia document declares what it is with the attributesSchema, childAttributesSchema and schemaDefinition fields, with a worked example, the child-inheritance rule and what the editor does with a typed document.

Three fields, three meanings

A document's metadata may carry up to three schema fields. The base document's metadata declares them, and each one says something different:

field

the sentence it says

value

attributesSchema

"This document's attributes follow that schema."

an hm:// document URL or ipfs://<cid>

childAttributesSchema

"My children's attributes follow that schema."

an hm:// document URL or ipfs://<cid>

schemaDefinition

"This document defines a schema others can reference."

ipfs://<cid> of a schema blob

People most often misread the last one. schemaDefinition marks a document as the home page of a schema. A document following a schema uses attributesSchema. A document that describes what a person is sets schemaDefinition. A document about one person, Bob, sets attributesSchema and points it at the person document. A value is never a type.

A worked example

Suppose the Acme account wants person pages.

    Acme publishes a schema blob: a struct with a required surname and an optional givenName. The blob has a CID.

    Acme publishes a readable page at hm://acme/person that explains what a person page is, with schemaDefinition = ipfs://<that cid>. This page is now the person type, and its hm:// URL names it.

    Acme publishes hm://acme/people/bob with attributesSchema = hm://acme/person. The app fetches the person document, follows its schemaDefinition to the blob, and learns that Bob's page must carry a surname.

    Acme sets childAttributesSchema = hm://acme/person on hm://acme/people. Every child created under it is a person page by default, so nobody has to set attributesSchema on each one.

The library ships this exact shape as an example. example/person-doc is an attributes schema, and example/bob is a live instance document whose attributesSchema points at its type.

The effective schema

One rule decides a document's effective attributes schema. It is the document's own attributesSchema if it has one, otherwise its parent's childAttributesSchema, otherwise none. A child that declares its own attributesSchema under a parent with a childAttributesSchema must satisfy both.

This rule types a whole directory without every page repeating the binding. It also lets one page opt out, or opt into something more specific, explicitly.

An attributes schema is a struct

An attributes schema is an ordinary struct with one property per attribute. It says nothing about documents. It does not extend the base document and never mentions metadata or content. Here is the person schema from the library, in dag-json:

{ "type": "hm://hyper.media/struct", "properties": { "surname": {"value": {"type": "hm://hyper.media/string"}, "required": true}, "givenName": {"value": {"type": "hm://hyper.media/string"}} } }

When a document is checked, the base metadata fields are folded in beneath the type's own fields: name, summary, icon and the three binding keys. The result stays open to extra keys. So a typed document is still a full document with a body, embeds, queries and comments, and the schema types only its attributes. To build one type on another, extend the struct: example/employee is example/person plus an employeeId.

What the editor does with it

Once a document has an effective schema, the Seed app changes in four visible ways:

    Required attributes are always present. Each required field from the resolved schema is a fixed row you cannot remove. It appears at the top of the Attributes tab and above the body in the Content tab, so a person page cannot lose its surname by accident.

    Fields get the right control. A field whose format is a Hypermedia URL renders as a searchable, clickable title pill. When the field names a target, the search offers documents typed by that schema or a subtype of it first, folder-typed children included, and a reference of another type shows a warning. A field whose format is an IPFS reference gets a file picker and a file pill. A union of literals becomes a dropdown. attributesSchema and childAttributesSchema are document-reference fields themselves, and icon, cover and schemaDefinition are IPFS-reference fields.

    Problems show in red and never block. A per-field badge and a summary banner list the actual violations, such as "surname is required" or "status must be one of draft, published, archived". Saving always works, because validation only warns. See why Hypermedia Schemas.

    You edit the schemas on the page. The Attributes tab opens with two sections. Attributes schema holds the fields this document carries, and Children attributes schema holds the fields every document created inside it carries. Each section shows the full schema editor when the document owns the schema. Edits stay in the draft. Publishing freezes each edited schema into a new IPFS object and points the binding key at it. A binding to a type page shows read-only with a link, and Edit a copy here takes it over. The options menu entries Attributes Schema and Children Attributes Schema open the tab on the matching section, and draft an empty struct when nothing is bound yet. So on a folder called Trees, one menu choice and one added field give every tree a height.

    A type's home page gets actions. A document carrying schemaDefinition shows a New Document button, which starts a draft whose attributesSchema is this page. It also shows a New Collection button, which starts a draft whose childAttributesSchema is this page. So a folder of people is one click from the person type. The options menu adds Extend Schema and, for developers, New Raw Value (a bare IPFS blob of the type) and Inspect Schema.

Dates, references and linked objects

Three kinds of field make a typed document work like a record:

    Dates. date and date-time are built-in refinements of string. A date is an ISO 8601 YYYY-MM-DD calendar date, and a date-time is an RFC 3339 instant. Each has a pattern, so a validator can check the shape. The editor shows a date picker, and the value on the wire is still the plain string.

    References with a target. A field whose format is hm-url or ipfs may carry a target: the schema the referenced document or object should conform to. character.home targets the Place type, and character.stats targets Character stats. A target is advisory, and the validator never dereferences a reference. The editor uses the target to pre-seed and validate what you create. See references and naming.

    Linked objects. An ipfs field can point at an uploaded file or at an object: a DAG-CBOR value you author in the Attributes editor. Press Create object on an empty field. With a target, the editor is locked to that type and publishes only a conforming value. Without one, you pick any schema (advisory) or choose free-form data. The published object carries a schema link to its type, the field is set to ipfs://<cid>, and the pill offers to open or edit the object. Blobs are immutable, so editing publishes a new object and re-points the field.

Doing it yourself

Turn on Developer Mode, then work from any document's options menu:

    New Schema opens the schema editor. Build the struct of attributes. Publishing mints the blob and gives you an ipfs:// CID.

    On the page that should be the type's home, set schemaDefinition to that CID in the Attributes editor. The page now shows the schema tag and the New Document and New Collection buttons.

    On a page that should be an instance, set attributesSchema to the home page's hm:// URL, or press New Document on the type's page. Required fields appear immediately.

    On a folder, set childAttributesSchema to the same URL to type everything beneath it, or press New Collection on the type's page. To type a folder's children without a separate type page, choose Children Attributes Schema from the folder's options menu and add the fields there.

Pinning or following

A reference by CID pins exact bytes. The type never changes under you, and you must republish to adopt a newer one. A reference by hm:// URL follows the type's document, which its owner may update. New fields then appear on every instance the next time it opens. The library uses names, so schemas can reference each other in cycles and a type can evolve in place. Choose on purpose: pin when you need a stable contract, and follow when you want the type's owner to improve it. References and naming covers the versioning trade-off.

See also

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime