Headlessly
Concepts

Digital Objects

The core abstraction — typed, versioned, event-sourced entities defined via the Noun() function.

Every entity in headless.ly is a Digital Object — a typed, versioned, event-sourced entity with an immutable history, relationships to other objects, and actions you can perform on it.

Defining a Noun

Entities are defined using the Noun() function from digital-objects. Zero dependencies. Zero codegen. Full TypeScript inference.

import { Noun } from 'digital-objects'

export const Contact = Noun('Contact', {
  name: 'string!',
  email: 'string?#',
  phone: 'string?',
  title: 'string?',
  stage: 'Lead | Qualified | Customer | Churned | Partner',
  source: 'string?',
  organization: '-> Organization.contacts',
  deals: '<- Deal.contact[]',
  messages: '<- Message.contact[]',

  qualify:  'Qualified',
  capture:  'Captured',
  assign:   'Assigned',
  merge:    'Merged',
  enrich:   'Enriched',
})

Property Value Patterns

Every property value tells the parser what it is:

PatternMeaningExample
Type stringData property'string!', 'number?', 'datetime!'
Arrow syntaxRelationship'-> Organization.contacts', '<- Deal.contact[]'
Pipe-separated PascalCaseEnum'Lead | Qualified | Customer'
Single PascalCase wordVerb declaration'Qualified', 'Captured'
nullOpt out of inherited verbupdate: null

Type Modifiers

ModifierMeaning
!Required
?Optional
#Indexed

Relationships

organization: '-> Organization.contacts'     // belongs to Organization
deals: '<- Deal.contact[]'         // has many Deals

-> is a forward reference (many-to-one). <- is a reverse reference (one-to-many). [] indicates a collection.

Immutability

Opt out of CRUD verbs with null:

export const Event = Noun('Event', {
  name: 'string!',
  timestamp: 'datetime!',
  update: null,
  delete: null,
})

Event.update() is a TypeScript error — the Noun definition above removes it.

On this page