Skip to content

Models

Model Definition

js
const Model = require('@igojs/db').Model;

const schema = {
  table:   'users',
  columns: [
    'id',
    'email',
    'password',
    'first_name',
    'last_name',
    'created_at'
  ]
};

class User extends Model(schema) {
  name() {
    return this.first_name + ' ' + this.last_name;
  }
}

module.exports = User;

The schema object defines the table name, columns, associations, and scopes.

Special column types

By default, columns are listed as plain strings — no type spec needed. For automatic conversion between database and JavaScript representations, declare the column as an object with type:

js
const schema = {
  table:   'users',
  columns: [
    'id',
    'first_name',
    'last_name',
    { name: 'is_validated', type: 'boolean' },
    { name: 'details_json', type: 'json', attr: 'details' },
    { name: 'pets_array', type: 'array', attr: 'pets' },
  ]
};
TypeBehavior
booleanAutomatically cast to true/false on load
jsonStringified on write, parsed on load. Access via attr name.
arrayJoined to CSV on write, split on load. Access via attr name.

Associations

Declare relationships in the schema with has_many and belongs_to:

js
const Project = require('./Project');
const Country = require('./Country');

const schema = {
  table: 'users',
  columns: ['id', 'country_id', 'email'],
  associations: [
    // [type, name, Model, localKey, foreignKey]
    ['has_many',   'projects', Project, 'id', 'user_id'],
    ['belongs_to', 'country',  Country, 'country_id'],
  ]
};

has_many from JSON array

has_many can reference an array of IDs stored as JSON:

js
const schema = {
  table: 'users',
  columns: [
    'id',
    { name: 'projects_ids_json', type: 'json', attr: 'projects_ids' },
  ],
  associations: () => ([
    ['has_many', 'projects', require('./Project'), 'projects_ids', 'id'],
  ])
};

Extra WHERE on associations

Filter associated records with an extra condition:

js
associations: [
  ['has_many', 'active_posts', Post, 'id', 'user_id', { active: true }],
]

See Queries for loading associations with includes() and join().

Scopes

Scopes add default query conditions:

js
const schema = {
  table: 'users',
  columns: ['id', 'email', 'status', 'created_at'],
  scopes: {
    default:   (query) => { query.order('`created_at` DESC'); },
    active:    (query) => { query.where({ status: 'active' }); },
  }
};

The default scope applies to all queries, including when using .scope() — named scopes stack on top of the default scope.

js
// default + active scopes both apply
const users = await User.scope('active').list();

// Remove all scopes (including default)
const all   = await User.unscope().list();

// Remove only the includes added by the default scope
const users = await User.unscope('includes').list();

// Replace default includes with specific ones
const users = await User.unscope('includes').includes('profile').list();

API Reference

Static Methods

MethodReturnsDescription
Model.create(values)instanceInsert a new record
Model.find(id)instance | nullFind by primary key
Model.first()instance | nullFirst record (respects default scope)
Model.last()instance | nullLast record (respects default scope)
Model.list()[instance, ...]All records
Model.count()numberCount records
Model.delete(id)Delete by primary key
Model.deleteAll()Delete all records
Model.update(values)Update all records
Model.where(conditions)QueryFilter records (see Queries)
Model.whereNot(conditions)QueryExclude records
Model.select(columns)QueryCustom SELECT
Model.distinct(columns)QueryDistinct values
Model.group(columns)QueryGROUP BY
Model.order(clause)QueryORDER BY
Model.limit(n)QueryLimit results
Model.offset(n)QuerySkip results
Model.page(page, perPage)QueryPaginate
Model.includes(assocs)QueryEager load associations
Model.join(assocs)QuerySQL JOIN
Model.scope(name)QueryApply named scope
Model.unscope(...clauses)QueryRemove all scopes (no args) or specific clauses

Instance Methods

MethodReturnsDescription
instance.update(values)Update this record
instance.delete()Delete this record
instance.reload(includes?)Refresh from database

CRUD

Create

js
const user = await User.create({
  first_name: 'John',
  last_name:  'Doe',
});
// => User { id: 1, first_name: 'John', last_name: 'Doe', ... }

If the primary key is AUTO_INCREMENT, it is set automatically on the returned instance.

create(values, options) accepts a second argument forwarded to the underlying driver query (silent, etc. — see the config page).

Find

js
const user = await User.find(id);
// Returns null if not found

First / Last

js
const user = await User.first();
const user = await User.last();
const user = await User.where({ status: 'active' }).first();

List

js
const users = await User.list();
const users = await User.where({ status: 'active' }).list();

Count

js
const total  = await User.count();
const active = await User.where({ status: 'active' }).count();

Update

js
// Update a single instance
const user = await User.find(id);
await user.update({ first_name: 'Jim' });

// Bulk update
await User.where({ country: 'France' }).update({ language: 'French' });

// Update all
await User.update({ status: 'inactive' });

Delete

js
// Delete by ID
await User.delete(id);

// Delete an instance
const user = await User.find(id);
await user.delete();

// Bulk delete
await User.where({ status: 'banned' }).delete();

// Delete all
await User.deleteAll();

Reload

Refresh an instance from the database:

js
const user = await User.find(id);
await user.reload();

// Reload with associations
await user.reload('country');

Hooks

Override beforeCreate and beforeUpdate for custom logic:

js
class User extends Model(schema) {
  async beforeCreate() {
    this.created_at = new Date();
  }

  async beforeUpdate(values) {
    values.updated_at = new Date();
  }
}

Single Table Inheritance

Support polymorphic models via subclass_column:

js
const schema = {
  table: 'users',
  columns: ['id', 'type', 'email'],
  subclass_column: 'type',
  subclasses: () => ({
    admin: require('./AdminUser'),
    user:  require('./RegularUser'),
  }),
};

Queries automatically return the correct subclass based on the type column.

Released under the ISC license.