Melon-dbMelon-db
Setup

Schema

Collections, fields, indexes, and relations in Melon-db.

Melon-db uses a code-first schema — a plain TypeScript object that describes every collection, field, index, and relation. Adapters turn this into SQLite tables; query compilers validate filters against it.

Schema shape

import { createMelonSchema, type DatabaseSchemaDefinition } from '@melon-db/db';

export const appSchemaDefinition: DatabaseSchemaDefinition = {
  version: 1,
  collections: {
    tasks: {
      name: 'tasks',
      primaryKey: 'id',
      fields: {
        id: { kind: 'string' },
        title: { kind: 'string' },
        status: { kind: 'string', indexed: true },
        priority: { kind: 'number' },
        projectId: { kind: 'string', nullable: true },
        updatedAt: { kind: 'date' },
      },
      relations: {
        project: {
          kind: 'belongsTo',
          target: 'projects',
          foreignKey: 'projectId',
        },
      },
      indexes: [['status'], ['updatedAt']],
    },
    projects: {
      name: 'projects',
      primaryKey: 'id',
      fields: {
        id: { kind: 'string' },
        name: { kind: 'string' },
      },
    },
  },
};

export const appSchema = createMelonSchema(appSchemaDefinition);

Pass appSchema (the runtime MelonSchema object) to createDatabase. Keep appSchemaDefinition if you need the raw definition for codegen or docs.

Field options

PropertyDescription
kindScalar type — see supported types
nullableAllow null in storage (default: required)
indexedSingle-column index hint on the field
defaultDefault value metadata (adapter-dependent)

Collection options

PropertyDescription
nameTable/collection name (usually matches the key in collections)
primaryKeyPrimary key field name (typically id)
fieldsMap of field name → FieldDefinition
relationsOptional belongsTo / hasMany metadata
indexesComposite indexes as arrays of field names
localOnlyWhen true, excluded from sync push/pull (requires sync: {} on the database)

Relations metadata

Relations are declared on the schema for typing and query include — they are not separate tables unless you model them that way.

relations: {
  project: {
    kind: 'belongsTo',
    target: 'projects',
    foreignKey: 'projectId',
  },
  tasks: {
    kind: 'hasMany',
    target: 'tasks',
    foreignKey: 'projectId',
  },
},
  • belongsTo — this row stores a foreign key pointing at another collection.
  • hasMany — the inverse: other rows point here via a foreign key.

Query-time include currently supports belongsTo only in v1. See Relations.

Schema version

version is a monotonic integer stored in adapter meta (_melon_meta). When you bump it:

  1. Add a matching migration with toVersion equal to the new version.
  2. If using sync, the client sends migration hints on pull so the backend can align schema changes.

Validation

createMelonSchema validates collection names, primary keys, and relation targets at construction time. Query compilers call validateQuery against the schema before execution.

  • Models — TypeScript record interfaces
  • Migrations — upgrading version
  • Query surfaces — all queries compile to AST validated against this schema