Selecting and populating
Return only the fields you need, and include related documents such as a post’s author in the same read.
A read returns every field the caller may read, and relationships as document IDs. Select fewer fields, or populate relationships to get the related documents in the same request. Examples use the example schema.
Select fields
Only each post’s title and author:
page, err := local.List(ctx, "posts", ridu.ListOptions{
Select: []query.Path{
query.Field("title"),
query.Field("author"),
},
Actor: user,
})const page = await ridu.list('posts', {
select: { title: true, author: true }
});curl -G "$RIDU/api/collections/posts" \
-H "Authorization: Session $TOKEN" \
--data-urlencode 'select={"title":true,"author":true}'{
"id": "posts_49d8d5f5eebb60b11c3fd9f9",
"title": "Hello, Ridu",
"author": "users_c15d8cdad9e97e3c8ba53543",
"createdAt": "2026-10-06T13:38:37.505562Z",
"updatedAt": "2026-10-06T13:38:37.505562Z"
}id, createdAt and updatedAt are always included. A selected relationship stays an ID until you
populate it. Select top-level fields; REST accepts at most 256.
A selection only shapes the result. Read hooks and field read rules still receive the whole document, so a rule that checks another field gives the same answer whichever fields you select.
In TypeScript, a literal select narrows the result’s type to the selected fields. If you build
the options separately, keep them literal with as const or satisfies, or the type stays broad.
Populate a relationship
Each post with its author’s name:
page, err := local.List(ctx, "posts", ridu.ListOptions{
Populate: []query.Population{{
Path: query.Field("author"),
Select: []query.Path{query.Field("name")},
}},
Actor: user,
})const page = await ridu.list('posts', {
populate: { author: { select: { name: true } } }
});curl -G "$RIDU/api/collections/posts" \
-H "Authorization: Session $TOKEN" \
--data-urlencode 'populate={"author":{"select":{"name":true}}}'{
"id": "posts_49d8d5f5eebb60b11c3fd9f9",
"title": "Hello, Ridu",
"author": {
"id": "users_c15d8cdad9e97e3c8ba53543",
"name": "Ada Lovelace",
"createdAt": "2026-10-06T13:38:22.661404Z",
"updatedAt": "2026-10-06T13:38:22.661404Z"
}
}Leave out select to get the whole related document. Ridu reads each related document with the
caller’s own access, so a document they can’t read stays out, and fields they can’t read are
removed. As with a top-level select, the related collection’s field read rules still see the
whole document. In Go, a populated relationship’s value is a store.Populated document.
In TypeScript, a literal populate changes the relationship’s type from an ID to the related
document, including the fields you selected.
Go deeper
Populate a relationship of the related document with depth, up to 5 levels:
const page = await ridu.list('posts', {
populate: { author: { depth: 2 } }
});In Go, set Depth on the query.Population. A relationship inside a group, array or block uses its
dotted path, such as 'sections.quote.source': true.
Populate every relationship
depth on its own populates every top-level relationship to that many levels:
const page = await ridu.list('posts', { depth: 1 });curl -G "$RIDU/api/collections/posts" \
-H "Authorization: Session $TOKEN" \
--data-urlencode 'depth=1'Use it for quick reads. For production queries, name the relationships with populate: the result’s
shape and cost stay predictable, and TypeScript knows the populated types. You can’t combine
depth with populate. In Go, list each relationship as a query.Population.
Limits
One request can name at most 64 relationship paths and populate to depth 5. Across the whole request, Ridu expands at most 256 schema paths and reads at most 4,096 related documents. These are limits for the request, not for each document.