Using Child Models in Sequelize.js

Updated: December 29, 2023 By: Guest Contributor Post a comment

Introduction

Sequelize.js is a powerful ORM for Node.js applications that provides easy management of database transactions, relationships, and more. In complex applications, you may encounter scenarios where you need to define models that inherit from a common parent – a kind of ‘Child-Parent’ model structure inherently supported by Sequelize through various methods. In this guide, we’ll explore how to properly use child models in Sequelize, diving into basic implementations and scaling up to more advanced use cases, to empower your application’s data models with OOP principles.

Basic Implementation of Child Models

To get started with child models in Sequelize, you first need to understand how single table inheritance can be implemented in SQL databases. A child model in Sequelize is generally a model that extends a parent model with the use of the extends keyword in ES6.

Let’s create a basic User model and extend it to create Admin and Customer models.

const { Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model {}

User.init({
  username: DataTypes.STRING,
  email: DataTypes.STRING
}, { sequelize, modelName: 'user' });

class Admin extends User {}
Admin.init({}, { sequelize, modelName: 'admin' });

class Customer extends User {}
Customer.init({}, { sequelize, modelName: 'customer' });

Practice Subclassing with Methods Override

In object-oriented programming, child models may override methods of their parent models. In Sequelize, you can achieve this by defining methods in a child class. Here, we’ll override a method in Admin to change its behavior compared to User.

class Admin extends User {
  static describeRole() {
    return 'Admin users have full access to the system.';
  }
}

console.log(Admin.describeRole()); // Admin users have full access to the system.

Advanced Inheritance with Scopes and Associations

As you advance in child models, you might want to have automatic embedded scopes or differing associations. Sequelize enables you to establish these with intricacies. Here is an example of how to define different scopes and associations for Admin and Customer, which are only relevant to their instances.

User.addScope('withOrders', {
  include: [{ model: Order }]
});

Customer.addScope('defaultScope', { ...User.scope('withOrders'), where: { type: 'customer' } }, { override: true });
Admin.addScope('defaultScope', { ...User.scope(), where: { type: 'admin' } }, { override: true });

Utilizing Model Hooks and Adding Custom Methods

Model hooks are functions that run at various stages of the lifecycle of an instance. You can define a unique hook for child models if there’s certain functionality triggered pre or post an operation like saving or updating records.

Admin.afterCreate((adminUser, options) => {
  console.log(`Admin ${adminUser.username} was created`);
});

Besides, you might need custom instance or class-level methods for child models. These methods can be highly specific to the extended model and therefore, their functionality can be tailored as such:

Customer.prototype.getAccountInfo = function () {
  return `${this.username}'s Subscription ends on ${this.subscriptionEndDate}`;
};

Conclusion

In conclusion, Sequelize.js allows you to elegantly structure your models with child-parent relationships, providing you with the tools to bring the principles of OOP into your applications’ data models. While starting with simple inheritance can be easy, mastering scopes, associations, hooks, and custom methods takes practice and understanding of Sequelize’s rich feature set. As long as you test and iteratively develop your models, working with child models should greatly enhance the scalability and maintainability of your codebase.