Models

Models represent database tables and provide CRUD operations. They extend Objection.js Model and are registered in a profile for lazy access throughout the application.

Creating a model

Create a file that defines a class extending the base Model:

// src/models/my-model/my-model.ts
import { Model } from '../_model/_model';

export class MyModel extends Model {
  static tableName: string = 'my_model';

  declare id: string;
  declare title: string;
  declare json: any;
}

Custom id column

If the primary key is not the default id, override idColumn:

static idColumn: string = 'uuid';

For composite primary keys, return an array:

static get idColumn(): string[] {
  return ['document', 'path'];
}

Relations

Define relations using Objection’s relationMappings. Use models.get() inside the getter to avoid circular imports:

static get relationMappings(): any {
  const Related = models.get('RelatedModel');
  return {
    _related: {
      relation: Model.BelongsToOneRelation,
      modelClass: Related,
      join: { from: 'my_model.related_id', to: 'related.id' },
    },
  };
}

Supported relation types: BelongsToOneRelation, HasManyRelation, ManyToManyRelation.

Custom collection

If the model needs a custom collection class for serialization, set the collection static:

import { MyCollection } from '../../collections/my-collection/my-collection';

export class MyModel extends Model {
  static collection = MyCollection as unknown as (typeof Model)['collection'];
}

Database migration

Create a migration file in src/migrations/ to create the table:

// src/migrations/202605270000_my_model.ts
import { Knex } from 'knex';

export const up = async (knex: Knex): Promise<void> => {
  await knex.schema.createTable('my_model', (table) => {
    table.string('id').primary();
    table.string('title');
    table.jsonb('json');
  });
};

export const down = async (knex: Knex): Promise<void> => {
  await knex.schema.dropTable('my_model');
};

Run the migration with pnpm knex migrate:latest.

Registration

In a profile’s init() function, register the model with a factory function:

import models from '../../models';
import { MyModel } from '../../models/my-model/my-model';

// In init():
models.register('MyModel', () => MyModel);

The factory pattern is used to avoid circular dependency issues with relation mappings. After registration, any part of the application can access the model:

const MyModel = models.get('MyModel');
const items = await MyModel.fetchAll({}, { order: 'title' }, trx);
const item = await MyModel.create(data, {}, trx);

This site uses Just the Docs, a documentation theme for Jekyll.