Guides
Relations & populate
Resolve related documents in a single request with the populate query parameter - one-hop reference resolution that stays inside each caller's permissions.
Reference fields
A field of type reference on a collection stores the _id of a document in another collection in the same project - a foreign key. For example, a posts collection can have an author reference field pointing at an authors collection. By default a read returns the raw id in that field.
Populating references
Pass populate a comma-separated list of reference fields to resolve. Each named field is replaced with the full referenced document. Only fields declared as type reference on the collection can be populated - any other field name is ignored.
# One reference
GET /api/data/projects/{projectId}/collections/{collectionId}/data?populate=author
# Several at once
GET /api/data/projects/{projectId}/collections/{collectionId}/data?populate=author,category
# Also works on a single document
GET /api/data/projects/{projectId}/collections/{collectionId}/data/{documentId}?populate=author# One reference
GET /api/data/projects/{projectId}/collections/{collectionId}/data?populate=author
# Several at once
GET /api/data/projects/{projectId}/collections/{collectionId}/data?populate=author,category
# Also works on a single document
GET /api/data/projects/{projectId}/collections/{collectionId}/data/{documentId}?populate=authorWithout populate, the reference field is the raw id:
{
"_id": "665f0a...c31",
"title": "Hello world",
"author": "665e91...a02"
}{
"_id": "665f0a...c31",
"title": "Hello world",
"author": "665e91...a02"
}With ?populate=author, it is resolved into the referenced document:
{
"_id": "665f0a...c31",
"title": "Hello world",
"author": {
"_id": "665e91...a02",
"name": "Ada Lovelace",
"email": "ada@example.com"
}
}{
"_id": "665f0a...c31",
"title": "Hello world",
"author": {
"_id": "665e91...a02",
"name": "Ada Lovelace",
"email": "ada@example.com"
}
}Populate respects permissions
populate is never a way around access control. A referenced document is included only if the caller is allowed to read it in the referenced collection - the same collection permissions and row-level conditions that apply to a direct read of that collection are enforced on the populated lookup. If the caller has no read access to the referenced collection, or the specific referenced row is outside their row-level scope, the field resolves to null rather than leaking a document they could not have fetched directly.
A reference is also returned as null when it was never set, or when its stored value does not match an existing document (for example an integer or UUID foreign key carried over from an imported relational database that has no corresponding record).
SDK
populate is a query parameter on the data list and single-document read endpoints, so the SDK forwards it exactly like sort and filter. The same read that lists documents returns the populated shape when you include it:
// List with a reference resolved
const { data: posts } = await data.listData(projectId, collectionId, undefined, undefined, undefined, undefined, {
params: { populate: "author,category" },
});
// Single document with a reference resolved
const { data: post } = await data.getData(projectId, collectionId, documentId, {
params: { populate: "author" },
});// List with a reference resolved
const { data: posts } = await data.listData(projectId, collectionId, undefined, undefined, undefined, undefined, {
params: { populate: "author,category" },
});
// Single document with a reference resolved
const { data: post } = await data.getData(projectId, collectionId, documentId, {
params: { populate: "author" },
});You can also pass fields the same way to return only a projection of each document.
Scope: one hop, references only
populate resolves a single hop. It does not chain through a referenced document's own references, and it does not perform arbitrary multi-collection joins - for a genuinely relational slice, denormalize the data or keep a relational database for that part. It resolves foreign-key-style reference fields, which is the one-hop relation case the large majority of reads need.