Strapi Relations — One-to-One, One-to-Many, Many-to-Many, and Morph Relations
In this tutorial, you will learn how to connect content types using Strapi's relation system, including one-to-one, one-to-many, many-to-many, and polymorphic morph relations, so you can model complex real-world data relationships.
What You'll Learn
- The four types of database relations and when to use each
- How to create relations through the Content-Type Builder
- How relations affect API responses and how to populate them
- The difference between bidirectional and unidirectional relations
- How morph relations enable polymorphic associations
- Best practices for designing relation schemas
Why It Matters
Relationships between content types are where the power of a relational database shines. A recipe API without relations means every recipe must store its author's name, bio, and avatar inline. With relations, the author data lives in one place and every recipe references it. This eliminates duplicate data, ensures consistency, and enables powerful queries like "show me all recipes by this author."
Real-World Use
An e-commerce platform needs Products that belong to Categories, have multiple Images, are sold by Suppliers, and have Customer Reviews. Each Customer has many Orders. Each Order has many Products (via an order items pivot). Without relations, this data model would be impossible to maintain. With Strapi relations, each entity connects naturally and the API can populate nested data in a single request.
Learning Path
flowchart LR A["Content Types"] --> B["Fields"] B --> C["Relations
-- You are here"]:::current C --> D["Components"] D --> E["Content Lifecycle"] E --> F["REST API"] classDef current fill:#4945ff,color:#fff,stroke-width:2px
Understanding Relations
A relation defines how two content types connect to each other. In database terms, relations are implemented through foreign keys and join tables.
Strapi supports four relation types:
- One-to-one: One entry connects to exactly one other entry
- One-to-many: One entry connects to many other entries
- Many-to-many: Many entries connect to many other entries
- Morph (polymorphic): A relation that can point to multiple content types
One-to-One (One Way)
One-to-one means each entry in type A connects to exactly one entry in type B, and vice versa.
Example: A User has one Profile. A Profile belongs to one User.
// On the User content type, add a relation field:
// Type: relation, Relation: has one, Target: Profile
// This creates a one-way relation from User to Profile
// On the Profile content type, add:
// Type: relation, Relation: belongs to, Target: User
// This creates a reverse relation back to User
When you create a relation in Strapi, you always specify both sides. Strapi automatically manages the foreign key. The API response includes the related data when you use the populate parameter.
One-to-Many
One-to-many means one entry in type A can have many entries in type B, but each entry in type B belongs to exactly one entry in type A.
Example: An Author has many Articles. Each Article belongs to one Author.
// On the Author content type:
// Relation: has many articles -> Article
// On the Article content type:
// Relation: belongs to author -> Author
// Strapi automatically creates the author_id foreign key on the articles table
This is the most common relation type. A blog with authors and posts. A category with products. A customer with orders.
// API response with populated relation
GET /api/articles?populate=author
// Response:
{
"data": [
{
"id": 1,
"attributes": {
"title": "Classic Margherita Pizza",
"author": {
"data": {
"id": 1,
"attributes": {
"name": "Alice",
"bio": "Italian cuisine expert"
}
}
}
}
}
]
}
Many-to-Many
Many-to-many means entries in type A can connect to many entries in type B, and vice versa. Strapi creates a join table to manage the connections.
Example: An Article can have many Tags. A Tag can belong to many Articles.
// On the Article content type:
// Relation: has many tags -> Tag
// On the Tag content type:
// Relation: has many articles -> Article
// Strapi creates a join table: articles_tags__tags_articles
// This table stores the pairs of article_id and tag_id
Many-to-many relations use a join table in the database instead of a foreign key column. This is because a single foreign key column cannot store multiple values.
Use many-to-many when:
- A recipe can have multiple tags, and a tag can apply to multiple recipes
- A product can be in multiple categories, and a category can contain multiple products
- A student can be in multiple courses, and a course can have multiple students
Understanding Relation Direction
Every relation in Strapi has a direction. You create it from one content type and configure the reverse side automatically.
One-way relations exist when you only need to access the relation from one side. Two-way (bidirectional) relations let you access from both sides.
// One-way: Author -> Article (Author knows about Articles, but Articles do not know about Author)
// Two-way: Author <-> Article (both know about each other)
// In the admin panel, Strapi creates two-way relations
// by default so both sides of the relation are accessible
Morph (Polymorphic) Relations
Morph relations are advanced. They let a field point to different content types.
Example: A "Comments" section can be attached to Articles, Videos, or Products. Instead of creating separate "article_comments", "video_comments", and "product_comments" types, one "Comment" type can morph to any of them.
// Create a Comment component or content type with:
// Relation: morphToMany
// This creates:
// - target_id (the ID of the related entry)
// - target_type (the content type name, e.g., "api::article.article")
// On Article, Video, and Product, add:
// Relation: morphMany comments -> Comment
The morph relation stores both the ID and the content type name in the database, enabling the polymorphic behavior.
You might be wondering when to use morph instead of standard relations. Use morph when a content type needs to connect to multiple different types. Without morph, you would need separate relation fields for each possible target type.
Populating Relations in API Requests
Relations are not included in API responses by default. You must explicitly populate them.
// Single level population
GET /api/articles?populate=author
// Deep population (two levels)
GET /api/articles?populate[author]=profile
// Multiple relations
GET /api/articles?populate=author,tags,category
// Populate all relations (use carefully)
GET /api/articles?populate=*
The wildcard populate=* loads every relation at every level. This is convenient for development but dangerous in production because it can return massive response sizes and slow database queries.
Preventing Common Relation Problems
Circular relations happen when two content types reference each other in a loop. Strapi handles circular relations at the schema level but you need to be careful when populating deeply.
// Circular relation: Article -> Author -> Article
// When populating deeply, Strapi can loop forever
// Strapi prevents this by limiting population depth
// Best practice: only populate to the depth you need
GET /api/articles?populate[author][populate][articles]=author
// This is risky and should be avoided
Common Mistakes
Not populating relations in API requests. Beginners expect related data to appear automatically. Relations are returned as empty objects unless explicitly populated via the
populateparameter.Using many-to-many where one-to-many suffices. If a product belongs to one category (not multiple), use one-to-many. Many-to-many adds unnecessary join table overhead and complexity.
Creating duplicate relation fields. Creating a relation on both content types independently instead of using Strapi's relation builder. This creates two separate relations instead of one bidirectional relation.
Overusing morph relations. Morph relations are powerful but complex. Use standard one-to-many or many-to-many unless you genuinely need polymorphic behavior. Morph relations are harder to query and debug.
Populating everything with wildcard. Using
populate=*in production causes slow API responses and heavy database load. Always specify exactly which relations to populate.
Practice Questions
What is the difference between one-to-many and many-to-many relations? Answer: In one-to-many, each child belongs to one parent (foreign key on child table). In many-to-many, children can belong to many parents and vice versa (join table required).
Why are relations not included in API responses by default? Answer: To prevent massive response sizes and slow queries. Populating relations requires additional database queries. Strapi lets you choose which relations to populate per request.
What is a morph relation and when would you use it? Answer: A morph relation allows a content type to connect to multiple different content types. Use it for comments, likes, or media attachments that can belong to various content types.
Challenge: Model the data for a university system with Students, Courses, Instructors, and Departments. A student can take many courses. A course has many students and one instructor. An instructor belongs to one department. A department has many instructors and courses. Create all the content types and relations in Strapi, add sample data, and write API queries that populate the relations to three levels of depth.
FAQ
Mini Project
Your task: Build a complete relational data model for a blog platform.
- Create content types: Author (name, bio, avatar), Article (title, content, published_at), Category (name, description), Tag (name), Comment (content, author_name).
- Set up relations:
- Author has many Articles. Article belongs to Author.
- Category has many Articles. Article belongs to Category.
- Article has many Tags. Tag has many Articles (many-to-many).
- Using morph: Comment morphs to Article.
- Add sample data: 2 authors, 5 articles, 3 categories, 8 tags, and several comments.
- Test API queries with various populate combinations.
- Write a query that returns articles with author, category, tags, and comments all populated.
What's Next
Now that you understand relations, proceed to Components & Dynamic Zones to learn how reusable field groups and flexible layout blocks add power to your content models. After that, explore Content Lifecycle for draft/publish and workflow management.
Related lessons:
- REST API Parameters — Populating relations in queries
- GraphQL — Querying relations in Graphql
- Node.js — How Strapi manages foreign keys
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro