Sequelize belongsTo: Load Related Records with Include
Use Sequelize belongsTo with the include option to fetch related records without manual joins. Match foreignKey exactly to your schema.
03 Sept 2026, 16:08 UTC

Fetch Related Records Without Writing Joins
The practical takeaway is that Sequelize's belongsTo association lets you retrieve a record and its related parent using the include option, instead of writing manual SQL JOIN syntax. A belongsTo association declares that the source model's table stores a foreign key column pointing to the target model's primary key. Sequelize resolves the relationship at query time and builds the required JOIN internally, so your application code works with nested model instances rather than flat result rows.
Configure the Association
Imagine a Post table with a user_id column referencing the id column of a User table. Define the relationship on the Post model after both models are initialized and before you run queries that depend on it:
Post.belongsTo(User, {
foreignKey: 'user_id',
as: 'author'
});
The foreignKey value must match the exact column name in your database schema. The as option assigns an alias, so the loaded user appears under post.author rather than under the default model name.
Query with Include
Run the query in your route or service file. Sequelize builds the JOIN implicitly based on the association metadata, so you do not write the join condition yourself:
const post = await Post.findOne({
where: { id: 1 },
include: [{ model: User, as: 'author' }]
});
The result nests the matching User instance under post.author. If no row satisfies the foreign key relationship, author is null.
Verify the Result
After the query resolves, inspect the nested property on the returned instance. Because findOne returns a single model instance or null, check for null before accessing nested properties:
if (post.author) {
console.log(post.author.userName);
} else {
console.log('No author found for this post.');
}
If author is unexpectedly null, confirm that user_id exists on the Post table, that the referenced User row exists, and that the as alias matches between the association definition and the include option.
Diagnostic Checklist
- Is
authornull? Check thatuser_idon the post row matches an existingidin theUsertable. - Did the query throw a column error? Confirm
foreignKeymatches the actual database column name, not the model property name. - Is the nested data missing entirely? Verify the
asalias in the association definition matches theasvalue ininclude.
Control Join Type and Loaded Fields
By default, include uses a left outer join, so a post is returned even when it has no matching user. Set required: true to use an inner join and exclude posts without authors. This is useful when every post must have an author and you want to filter out orphaned rows at the database level:
const post = await Post.findOne({
where: { id: 1 },
include: [{
model: User,
as: 'author',
required: true,
attributes: ['id', 'userName', 'email']
}]
});
The attributes array restricts which columns Sequelize selects from the related table, reducing payload size. Omitting it selects all columns by default.
Manual Join vs. Association Include
| Approach | Code | Result Shape |
|---|---|---|
| Raw join | sequelize.query('SELECT ... FROM posts JOIN users ...') | Flattened rows; you map columns manually |
include | Post.findOne({ include: [{ model: User, as: 'author' }] }) | Nested model instances accessible via post.author |
Limits and Common Mistakes
- Foreign key mismatch: Sequelize does not check the column against your live database at runtime. A typo in
foreignKeycauses a query failure or silently missing data. - No database constraint: Declaring
belongsToin Sequelize does not create a foreign key constraint in the database. Add constraints through migrations or your database client. - Alias mismatch: The
asvalue in the association definition and theincludeoption must match exactly. - Over-eager loading: Including several large associations in one query can degrade response time. Load only the relationships you need.
This behavior applies to Sequelize v6 and v7 in standard Node.js applications. Defining associations and running read queries do not modify database state, so no rollback procedure is required. Always test association queries against a staging database that mirrors production schema before deploying changes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.