Relations
Model associations between resources with foreign key fields.
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."
Defining a relation
Add a field of type Relation to your resource and configure three things on
the field: the target resource, the target field on that resource (typically
id), and the relation kind.
Relation properties
| Property | Description |
|---|---|
targetResource |
Name of the related resource (required). |
targetField |
Field on the related resource to join on. |
kind |
One of the four relation kinds listed below. |
Relation kinds
One-to-one
Each record in the source resource maps to exactly one record in the target
resource, and vice versa. Example: a users resource has one profiles record.
One-to-many
A single record in the source resource can be referenced by many records in the
target resource. This is defined on the "one" side. Example: a categories
resource has many products.
Many-to-one
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
kind. Example: many posts belong to one users record.
Many-to-many
Records on both sides can reference multiple records on the other side. Snaapi
manages the join internally. Example: posts can have many tags, and each
tags record can appear on many posts.
Query behavior
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": "[email protected]"
}
}
]
}
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.
Blog example: all four relation kinds together
Here is how the four relation kinds combine in a realistic blog application with
posts, authors, profiles, tags, and categories resources.
- Posts to authors (many-to-one). Each post has exactly one author, but an
author can write many posts. Add an
author_idrelation field onpostspointing atauthors. - Authors to profiles (one-to-one). Each author has a single profile
containing their bio and avatar. Add a
profile_idrelation field onauthorspointing atprofiles. - Posts to tags (many-to-many). A post can have multiple tags, and each tag
can appear on many posts. Add a
tag_idsrelation field onpostspointing attags. - Categories to posts (one-to-many). A category contains many posts. Add a
category_idrelation field onpostspointing back atcategories.
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.