Introduction
Sequelize is a promise-based Node.js Object-Relational Mapper (ORM) for Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. It abstracts the complexities of direct database interaction, making it easier to manage the application’s data layer. A key concept in database schema design is the use of primary keys, which uniquely identify a record in a table. Sometimes a single field is insufficient to uniquely identify a record, in which cases a composite primary key, made up of two or more columns, is used. In this tutorial, we will dive deep into how Sequelize deals with composite primary keys, looking at basic to advanced examples alongside relevant use cases.
What are Composite Primary Keys?
In relational database design, a primary key is a field (or a set of fields collectively) that uniquely identifies a record within a table. A composite primary key is created when you utilize two or more fields to establish a unique constraint. This is often needed in situations where single-column primary keys do not provide enough uniqueness or when it suits the business logic to define uniqueness across a combination of columns.
An effective composite key provides not only a way to uniquely identify records but also to enforces database integrity and affects the performance of database operations. Which fields comprise a composite key depends entirely on the specific needs of the database schema and how related data entities interact with one another within the system.
Setting up Sequelize and a Database Connection
// Installation
npm install --save sequelize
npm install --save pg pg-hstore // For PostgreSQL
// Import Sequelize
const Sequelize = require('sequelize');
// Set up a connection
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'host',
dialect: 'postgres' // or 'mysql', 'sqlite', 'mariadb', 'mssql'
});
Defining Models with a Composite Primary Key
To demonstrate the use of composite primary keys in Sequelize, let’s start with a basic example. Imagine we have a many-to-many relationship between Users and Groups. Instead of creating a separate id column for the join table, we can use a composite key that consists of the user ID and the group ID.
const UserGroup = sequelize.define('user_group', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true
},
groupId: {
type: Sequelize.INTEGER,
primaryKey: true
}
});
In the model definition above, we’ve created a `UserGroup` model with two primary key fields: `userId` and `groupId`. This establishes a composite primary key in our join table.
Advanced Use Cases for Composite Primary Keys
Composite primary keys can be particularly useful in scenarios such as certain tagging systems, where you have a restricted set of tags and a record can have many tags leading to a polymorphic association, or purely from a modeling perspective where the identity of the record is naturally composite.
// Tagging system model
const Taggable = sequelize.define('taggable', {
tagId: {
type: Sequelize.INTEGER,
primaryKey: true
},
taggableId: {
type: Sequelize.INTEGER,
primaryKey: true
},
taggableType: {
type: Sequelize.STRING,
primaryKey: true
}
});
In this fabricated tagging system example, the `Taggable` model’s primary key is composed of `tagId`, `taggableId`, and `taggableType`. This kind of set up allows for a flexible tagging system across different model types.
Things to Consider When Using Composite Keys
While composite primary keys are powerful, they are not without their trade-offs. One of the main considerations is that they can complicate the queries against your database, especially when it comes to writing raw queries outside of Sequelize or creating efficient indexes. Furthermore, they can also affect the REST API framework design, potentially requiring more complex endpoint structures to accommodate composite keys.
It is worth noting that when working with Sequelize associations, especially many-to-many, Sequelize naturally handles the use of a composite primary key under the hood. When manually managing association models, however, you must be mindful of how those keys are being used and how to properly define them for optimum database performance and integrity.
Conclusion
This tutorial introduced and explained how to work with composite primary keys using Sequelize.js. While the examples provided touched on the basics and offered insights into more complex scenarios, the overarching theme was that Sequelize streamlines the use of composite keys as much as possible, but with the power of this feature comes the responsibility to be mindful of database design principles and the potential implications on your application’s performance and REST API structure.