NOML Specification

The complete reference for NoSQL Modeling Language

Version 1.0.0 — Firestore

Versioning & Format

Current version1.0.0
Version schemeSemantic Versioning 2.0.0 — MAJOR.MINOR.PATCH
Document formatYAML 1.2 / JSON
EncodingUTF-8
File extension.noml.yaml .noml.yml .noml.json
Target databaseFirestore (MVP) — MongoDB, DynamoDB, etc. planned

Design Principles

Lenient Parsing

Unknown fields are silently ignored. This allows forward compatibility and custom extensions.

Minimal Required Fields

Only version, database, and collections (with fields.type) are required. Everything else is optional.

Terminology

Document
A single record in a collection, analogous to a row in SQL.
Collection
A group of documents, analogous to a table in SQL.
Subcollection
A collection nested within a document.
Field
A named property of a document.
Enum
A set of predefined allowed values. Can include state machine transitions.
Reference
A field that points to another document in a different collection.
Index
A database structure that improves query performance, especially composite indexes in Firestore.

Required vs Optional

NOML requires only the minimum fields to produce a valid schema:

version

NOML version

database

Database type

collections

With fields.type

Everything else — metadata, enums, keys, indexes, security, validation, label, description, etc. — is entirely optional.

Field Reference

Root — NomlSchema

FieldRequiredTypeDescription
versionRequiredstringNOML version (e.g. "1.0.0")
databaseRequiredstringDatabase type (e.g. "firestore")
collectionsRequiredRecord<string, CollectionDef>Collection definitions
metadataOptionalMetadataDefProject metadata
descriptionOptionalstringSchema description
updatedAtOptionalstringLast update date (ISO 8601)
enumsOptionalRecord<string, EnumDef>Global enum definitions

Metadata — MetadataDef

FieldRequiredTypeDescription
nameRequiredstringSchema/project name
descriptionOptionalstringDetailed description
authorOptionalstringAuthor or team name

Collection — CollectionDef

FieldRequiredTypeDescription
fieldsRequiredRecord<string, FieldDef>Field definitions
labelOptionalstringLogical name (e.g. Japanese name)
descriptionOptionalstringCollection description
pathOptionalstringFirestore path pattern (e.g. "users/{userId}")
keysOptionalKeysDefKey constraints (primary, foreign, unique)
subcollectionsOptionalRecord<string, CollectionDef>Nested subcollection definitions
indexesOptionalIndexDef[]Composite index definitions
securityOptionalSecurityDefSecurity rules

Field — FieldDef

FieldRequiredTypeDescription
typeRequiredstringField data type (Firestore type or enum name)
labelOptionalstringLogical name (e.g. Japanese name)
descriptionOptionalstringField description
requiredOptionalbooleanWhether field is required (default: false)
nullableOptionalbooleanWhether null is allowed
defaultOptionalanyDefault value
exampleOptionalanyExample value for documentation
sourceOptionalstringData source (e.g. "documentId")
immutableOptionalbooleanCannot be updated after creation
autoUpdateOptionalbooleanAuto-update on each write
targetOptionalstringTarget collection for reference type
itemsOptionalstring | objectArray item type definition
fieldsOptionalRecord<string, FieldDef>Nested fields for map type
validationOptionalValidationDefValidation constraints
denormalizedFromOptionalobject | stringSource of denormalized data
denormalizationOptionalobjectDenormalization targets and sync method

Enum — EnumDef

FieldRequiredTypeDescription
valuesRequiredarrayAllowed values (strings, numbers, or objects with value/label/description)
labelOptionalstringLogical name
descriptionOptionalstringEnum description
transitionsOptionalRecord<string, string[]>State machine transitions (from → to[])

Use the enum name directly as the field type:

enums:
  UserRole:
    values: [user, admin, moderator]

collections:
  users:
    fields:
      role:
        type: UserRole  # enum name as type
        default: user

Keys — KeysDef

FieldRequiredTypeDescription
primaryOptionalstringPrimary key field name
descriptionOptionalstringKeys description
foreignOptionalForeignKeyDef[]Foreign key definitions (field, references, onDelete)
uniqueOptionalUniqueKeyDef[]Unique key definitions (field, enforceBy)
compositeUniqueOptionalCompositeUniqueDef[]Composite unique key definitions (fields, enforceBy)

Index — IndexDef

FieldRequiredTypeDescription
fieldsRequiredIndexFieldDef[]Fields to index (field, order, arrayContains)
nameOptionalstringIndex name
descriptionOptionalstringIndex description
queryExampleOptionalstringExample query using this index

Security — SecurityDef

FieldRequiredTypeDescription
readOptionalSecurityRuleDef[]Read operation rules (condition, description, excludeFields)
createOptionalSecurityRuleDef[]Create operation rules
updateOptionalSecurityRuleDef[]Update operation rules
deleteOptionalSecurityRuleDef[]Delete operation rules

Validation — ValidationDef

FieldRequiredTypeDescription
minOptionalnumberMinimum value (for number)
maxOptionalnumberMaximum value (for number)
minLengthOptionalnumberMinimum string length
maxLengthOptionalnumberMaximum string length
patternOptionalstringRegex pattern
formatOptionalstringPredefined format (email, url, uuid, date, phone)
minItemsOptionalnumberMinimum array length
maxItemsOptionalnumberMaximum array length
enumOptionalarrayInline allowed values (use global enums instead)

Denormalization

NoSQL databases often denormalize data for read performance. NOML supports documenting both the source and destination of denormalized data.

denormalizationSource field

displayName:
  type: string
  denormalization:
    targets:
      - collection: posts
        field: authorName
    syncMethod: cloudFunction

denormalizedFromDestination field

authorName:
  type: string
  denormalizedFrom:
    collection: users
    field: displayName

Data Types

TypeDescriptionExample
stringText data"Hello"
numberInteger or float42, 3.14
booleanTrue or falsetrue
timestampDate and time2024-01-15T10:30:00Z
geopointGeographic coordinates{ lat: 35.67, lng: 139.65 }
referenceDocument reference/users/abc123
arrayOrdered list["a", "b", "c"]
mapNested object{ key: "value" }

Special Values

ValueDescriptionUsage
serverTimestampServer-generated timestampdefault: serverTimestamp
autoIdAuto-generated document IDdefault: autoId

Specification Extensions

Vendor-specific extensions must use the x- prefix. Unknown fields are silently ignored.

collections:
  users:
    x-firebase-ttl: 86400
    fields:
      email:
        type: string
        x-algolia-searchable: true
      secretKey:
        type: string
        x-sensitive: true
        x-encryption: AES-256

Full Example

A complete NOML document using all major features — metadata, enums with transitions, collections, subcollections, references, keys, indexes, validation, security, and denormalization.

version: "1.0.0"
database: firestore
metadata:
  name: Blog Platform
  description: A full-featured blog with users, posts, and comments
  author: Platform Team

enums:
  UserRole:
    label: ユーザー権限
    description: User permission levels
    values:
      - value: user
        label: User
        description: Regular user
      - value: admin
        label: Administrator
  PostStatus:
    label: 投稿ステータス
    values: [draft, review, published, archived]
    transitions:
      draft: [review, archived]
      review: [draft, published]
      published: [archived]
      archived: []

collections:
  users:
    label: ユーザー
    description: Registered user accounts
    keys:
      primary: id
      unique:
        - field: email
          enforceBy: cloudFunction
    fields:
      id:
        type: string
        source: documentId
        required: true
      email:
        type: string
        label: メールアドレス
        required: true
        immutable: true
        validation:
          format: email
      displayName:
        type: string
        label: 表示名
        required: true
        validation:
          maxLength: 100
        denormalization:
          targets:
            - collection: posts
              field: authorName
          syncMethod: cloudFunction
      role:
        type: UserRole
        label: 権限
        default: user
      createdAt:
        type: timestamp
        label: 作成日時
        default: serverTimestamp
        immutable: true

    subcollections:
      settings:
        label: 設定
        description: User preferences
        fields:
          theme:
            type: string
            label: テーマ
            default: system
          language:
            type: string
            default: en

    security:
      read:
        - condition: "request.auth != null"
          description: Authenticated users can read
      update:
        - condition: "request.auth.uid == resource.data.id"
          description: Users can update their own profile
          excludeFields: [role, createdAt]

  posts:
    label: 投稿
    description: Blog posts
    keys:
      foreign:
        - field: authorId
          references: users.id
          onDelete: cascade
    fields:
      title:
        type: string
        label: タイトル
        required: true
        validation:
          maxLength: 200
      authorId:
        type: string
        required: true
        immutable: true
      authorName:
        type: string
        label: 著者名
        denormalizedFrom:
          collection: users
          field: displayName
      tags:
        type: array
        label: タグ
        items: string
        validation:
          maxItems: 10
      status:
        type: PostStatus
        label: ステータス
        default: draft
      createdAt:
        type: timestamp
        default: serverTimestamp
        immutable: true

    indexes:
      - name: posts_by_author
        fields:
          - authorId
          - field: createdAt
            order: desc
      - name: posts_by_tag
        fields:
          - field: tags
            arrayContains: true
          - field: createdAt
            order: desc

Try it in the viewer

Write your NOML and see it visualized instantly.

Open Viewer