Relationships
Learn how to define and use relationships between resources in api.vision to model complex data connections.
Understanding Relationships
Relationships define how different resources connect to each other. api.vision supports all common relationship types:
One-to-One
A resource has exactly one related resource, and vice versa.
Example: A user has one profile, and a profile belongs to one user.
One-to-Many
A resource has multiple related resources, but each related resource belongs to only one parent.
Example: A user has many posts, but each post belongs to one user.
Many-to-Many
A resource can have multiple related resources, and vice versa.
Example: A post can have many tags, and each tag can be applied to many posts.
Self-Referential
A resource has relationships with other resources of the same type.
Example: A user can follow other users, creating a self-referential many-to-many relationship.
Defining Relationships
In api.vision, relationships are defined in your resource specifications using special field attributes:
One-to-One Relationship
// One-to-one: User has one Profile
{
"resources": {
"users": {
"fields": {
"id": { "type": "id" },
"username": { "type": "string" },
"email": { "type": "string" },
"profile": { "type": "relation", "resource": "profiles", "relation": "hasOne" }
}
},
"profiles": {
"fields": {
"id": { "type": "id" },
"userId": { "type": "integer", "reference": "users.id" },
"bio": { "type": "string" },
"avatar": { "type": "string" },
"user": { "type": "relation", "resource": "users", "relation": "belongsTo" }
}
}
}
}
One-to-Many Relationship
// One-to-many: User has many Posts
{
"resources": {
"users": {
"fields": {
"id": { "type": "id" },
"username": { "type": "string" },
"email": { "type": "string" },
"posts": { "type": "relation", "resource": "posts", "relation": "hasMany" }
}
},
"posts": {
"fields": {
"id": { "type": "id" },
"title": { "type": "string" },
"content": { "type": "string" },
"userId": { "type": "integer", "reference": "users.id" },
"author": { "type": "relation", "resource": "users", "relation": "belongsTo" }
}
}
}
}
Many-to-Many Relationship
// Many-to-many: Posts have many Tags (and vice versa)
{
"resources": {
"posts": {
"fields": {
"id": { "type": "id" },
"title": { "type": "string" },
"content": { "type": "string" },
"tags": { "type": "relation", "resource": "tags", "relation": "belongsToMany", "through": "post_tags" }
}
},
"tags": {
"fields": {
"id": { "type": "id" },
"name": { "type": "string" },
"posts": { "type": "relation", "resource": "posts", "relation": "belongsToMany", "through": "post_tags" }
}
},
"post_tags": {
"fields": {
"id": { "type": "id" },
"postId": { "type": "integer", "reference": "posts.id" },
"tagId": { "type": "integer", "reference": "tags.id" }
}
}
}
}
Relationship Types
api.vision supports the following relationship types:
Relation Type | Description | Example |
---|---|---|
hasOne | A one-to-one relationship where the resource has one related resource | A user has one profile |
belongsTo | A one-to-one or many-to-one relationship where the resource belongs to one related resource | A profile belongs to one user |
hasMany | A one-to-many relationship where the resource has many related resources | A user has many posts |
belongsToMany | A many-to-many relationship where the resource belongs to many related resources | A post belongs to many tags |
Working with Relationships
Once relationships are defined, you can use them in various ways:
Expanded Responses
Use the expand
query parameter to include related resources in responses:
GET /posts?expand=author,tags
Nested Expansions
Expand multiple levels of relationships with dot notation:
GET /posts?expand=author.profile,comments.author
Filtering on Related Resources
Filter resources based on properties of related resources:
GET /posts?author.role=admin
Creating Resources with Relationships
When creating a resource, you can define its relationships:
// Create a post with author and tags
POST /posts
{
"title": "Getting Started with API Design",
"content": "...",
"authorId": 42,
"tags": [1, 5, 8]
}
Updating Relationships
Update a resource's relationships with PATCH requests:
// Update a post's tags
PATCH /posts/1
{
"tags": [2, 5, 9]
}
Advanced Relationship Features
Custom Keys
You can specify custom foreign keys for relationships:
"author": {
"type": "relation",
"resource": "users",
"relation": "belongsTo",
"foreignKey": "writer_id" // Custom foreign key
}
Pivot Data in Many-to-Many
Access pivot table data in many-to-many relationships:
// Definition
"tags": {
"type": "relation",
"resource": "tags",
"relation": "belongsToMany",
"through": "post_tags",
"withPivot": ["added_at", "added_by"]
}
// Query with pivot data
GET /posts/1?expand=tags
// Response
{
"id": 1,
"title": "Getting Started with API Design",
"content": "...",
"tags": [
{
"id": 5,
"name": "API Design",
"pivot": {
"added_at": "2023-04-15T14:32:10Z",
"added_by": 42
}
},
// ...
]
}
Self-Referential Relationships
Define relationships within the same resource:
// Self-referential: Comments can have replies
"comments": {
"fields": {
"id": { "type": "id" },
"text": { "type": "string" },
"parentId": { "type": "integer", "reference": "comments.id", "nullable": true },
"parent": { "type": "relation", "resource": "comments", "relation": "belongsTo" },
"replies": { "type": "relation", "resource": "comments", "relation": "hasMany", "foreignKey": "parentId" }
}
}
Polymorphic Relationships
Define relationships that can connect to multiple resource types:
// Polymorphic: Comments can belong to posts or products
"comments": {
"fields": {
"id": { "type": "id" },
"text": { "type": "string" },
"commentableId": { "type": "integer" },
"commentableType": { "type": "string" },
"commentable": {
"type": "relation",
"relation": "morphTo",
"typeField": "commentableType",
"idField": "commentableId"
}
}
},
"posts": {
"fields": {
"id": { "type": "id" },
"title": { "type": "string" },
"comments": {
"type": "relation",
"resource": "comments",
"relation": "morphMany",
"typeValue": "posts"
}
}
},
"products": {
"fields": {
"id": { "type": "id" },
"name": { "type": "string" },
"comments": {
"type": "relation",
"resource": "comments",
"relation": "morphMany",
"typeValue": "products"
}
}
}
Best Practices
- Define relationships on both sides for clarity (e.g., both hasMany and belongsTo)
- Use descriptive relationship names that reflect their purpose
- For many-to-many relationships, create a proper junction table with any additional pivot data
- Consider performance when defining deep relationship hierarchies
- Use expansion judiciously to avoid oversized response payloads