Relations define how resources reference each other. A relation field creates a foreign key link from one resource to another, enabling you to model associations like "a post belongs to a user" or "a student enrolls in many courses."
Add a field with type: "relation" and include a relations object specifying
the target resource, the target field, and the relation type:
{
"name": "author_id",
"type": "relation",
"relations": {
"resource": "users",
"field": "id",
"type": "many-to-one"
}
}| Property | Type | Description |
|---|---|---|
resource |
string | Name of the related resource (required) |
field |
string | Field on the related resource to join on |
type |
string | One of the four relation types listed below |
Each record in the source resource maps to exactly one record in the target resource, and vice versa.
{
"name": "profile_id",
"type": "relation",
"relations": { "resource": "profiles", "field": "id", "type": "one-to-one" }
}Use case: a users resource has one profiles record.
A single record in the source resource can be referenced by many records in the target resource. This is defined on the "one" side.
{
"name": "category_id",
"type": "relation",
"relations": {
"resource": "categories",
"field": "id",
"type": "one-to-many"
}
}Use case: a categories resource has many products.
Many records in the source resource point to a single record in the target resource. This is the inverse of one-to-many and is the most common relation type.
{
"name": "author_id",
"type": "relation",
"relations": { "resource": "users", "field": "id", "type": "many-to-one" }
}Use case: many posts belong to one users record.
Records on both sides can reference multiple records on the other side. Snaapi manages the join internally.
{
"name": "tag_ids",
"type": "relation",
"relations": { "resource": "tags", "field": "id", "type": "many-to-many" }
}Use case: posts can have many tags, and each tags record can appear on
many posts.
When you read a resource that contains relation fields, Snaapi resolves the
relations automatically. Related data is returned following the JSON:API spec
using relationships and included:
{
"data": {
"type": "posts",
"id": "post-uuid",
"attributes": {
"title": "Hello World",
"created_at": "2026-01-01T00:00:00Z"
},
"relationships": {
"author": {
"data": { "type": "authors", "id": "author-uuid" }
}
}
},
"included": [
{
"type": "authors",
"id": "author-uuid",
"attributes": {
"name": "Jane Doe",
"email": "jane@example.com"
}
}
]
}The included resources and visible fields depend on the permissions of the requesting role.
- Filtering. You can filter by relation fields using the standard filter
operators (e.g.,
author_id eq <uuid>). - Sorting. If the relation field is marked
sortable, results can be ordered by the foreign key value. - Permissions. Relation fields are subject to the same field-level permission rules as any other field. A role must have access to the relation field to see or filter by it.
- Existence checks. Use the
exists/not_existsfilter operators to query based on whether a related record is present.
Here is how the four relation types work together in a realistic blog
application with posts, authors, profiles, tags, and categories
resources.
Each post has exactly one author, but an author can write many posts. Add this
field to your posts resource:
{
"name": "author_id",
"type": "relation",
"required": true,
"relations": { "resource": "authors", "field": "id", "type": "many-to-one" }
}Each author has a single profile containing their bio and avatar. Add this
field to your authors resource:
{
"name": "profile_id",
"type": "relation",
"relations": { "resource": "profiles", "field": "id", "type": "one-to-one" }
}A post can have multiple tags, and each tag can appear on many posts. Add this
field to your posts resource:
{
"name": "tag_ids",
"type": "relation",
"relations": { "resource": "tags", "field": "id", "type": "many-to-many" }
}A category contains many posts. To represent this, add a relation field on
your posts resource that points back to categories:
{
"name": "category_id",
"type": "relation",
"relations": { "resource": "categories", "field": "id", "type": "one-to-many" }
}Together these relations let you query posts with their author, that author's profile, all associated tags, and the parent category. Snaapi resolves all of this automatically based on the requesting role's permissions.