The Sequelize hasOne()
association method is used to establish an association between two defined Sequelize models.
The association method allows you to link two models so that you can retrieve data from both tables with one query execution.
Let’s see an example of creating an association in Sequelize. Suppose you have two Sequelize models representing existing tables as shown below:
const User = sequelize.define(
"User",
{ firstName: Sequelize.STRING },
{ timestamps: false }
);
const Task = sequelize.define(
"Task",
{ taskName: Sequelize.STRING },
{ timestamps: false }
);
The above models represent Users
and Tasks
tables in your SQL database server. Please note that Sequelize uses the plural forms of the model names when looking for the tables by default.
The relationship between the models is that each row in the Users
table can have one Tasks
row.
You need to define the relationship between the tables in Sequelize models as follows:
User.hasOne(Task);
Using the hasOne()
method call, Sequelize will create an association between the Users
and the Tasks
tables.
The model where you call the hasOne()
method is called the source model, and the model passed as the argument is the target model.
The UserId
attribute will be added to the Task
model.
Keep in mind that defining the relationship by calling the hasOne()
method doesn’t alter the actual table and adds the foreign key constraint.
You need to call the sync()
method on the Task
model to let Sequelize make the necessary changes:
await Task.sync({ alter: true });
The Task.sync()
call above cause Sequelize to generate and execute the following SQL statement:
ALTER TABLE `Tasks`
ADD `UserId` INTEGER,
ADD CONSTRAINT `Tasks_UserId_foreign_idx`
FOREIGN KEY (`UserId`)
REFERENCES `Users` (`id`)
ON DELETE SET NULL ON UPDATE CASCADE;
Without calling the sync()
method, you need to use Sequelize migration or change the actual table definitions manually.
With the column and the constraint added to the SQL table, you can perform a JOIN
query in Sequelize using either a raw query or the include
option:
const [results, metadata] = await sequelize.query(
"SELECT * FROM Tasks JOIN Users ON Tasks.UserId = Users.id"
);
Learn more here: How to create JOIN queries with Sequelize
Customizing hasOne foreign key configurations
By default, Sequelize uses the primary key of the source model as the reference for the foreign key column.
You can change the foreign key reference, configurations, and constraints by specifying an options
object when calling the hasOne()
method.
Here’s an example of customizing the generated foreign key statement:
User.hasOne(Task, {
foreignKey: "task_owner", // change column name
sourceKey: "firstName", // change the referenced column
uniqueKey: "task_user_fk", // foreign key constraint name
onDelete: "RESTRICT", // ON DELETE config
onUpdate: "RESTRICT", // ON UPDATE config
constraints: false, // remove ON DELETE and ON UPDATE constraints
});
For the full list of available options, you can view the Sequelize hasOne documentation.
Making the foreign key index unique
Keep in mind that the hasOne()
method creates a non-unique index on the column that’s used to save the foreign key.
The method only makes sure that the target model (the Task
model in this case) has one associated data in the source model (User
).
If you need to add a unique constraint to the foreign key attribute or column, then you need to add the indexes
option in the target model as shown below:
const Task = sequelize.define(
"Task",
{ taskName: Sequelize.STRING },
{
timestamps: false,
indexes: [
{
unique: true,
fields: ["UserId"],
},
],
}
);
await Task.sync({ alter: true });
The fields
array value should define the names of the attributes/ columns where the unique constraint applies.
Once you configure the model, don’t forget to call the Task.sync({ alter: true })
again to synchronize the changes to the table.
Now the UserId
attribute/ column will be unique, and inserting a duplicate value will cause SQL to throw the Duplicate entry for key
error.
Now you’ve learned how the hasOne()
Sequelize method works and how to configure its options.
You also learned how to create a unique index constraint for the foreign key column using the indexes
option. Good work! 👍