Relations & Include
Use include to JOIN related records in a single query.
SuitePortal introspects relationships between records and lets you include related data in your queries using include, similar to Prisma's relation loading.
How It Works
When you pass include to findMany or findFirst, SuitePortal generates a LEFT JOIN and returns nested objects:
const orders = await ns.salesorder.findMany({
select: { id: true, tranid: true },
include: { entity: true },
take: 10,
});SELECT salesorder.id, salesorder.tranid,
entity.id AS entity__id,
entity.companyname AS entity__companyname,
entity.email AS entity__email
FROM salesorder
LEFT JOIN customer entity ON salesorder.entity = entity.id[
{
id: 1,
tranid: 'SO-001',
entity: {
id: 42,
companyname: 'Acme Corp',
email: 'hello@acme.com',
},
},
// ...
]Selective Include
By default, include: { entity: true } fetches all fields from the related record. You can narrow it with select:
const orders = await ns.salesorder.findMany({
select: { id: true, tranid: true },
include: {
entity: { select: { companyname: true, email: true } },
},
});This only fetches id, companyname, and email from the customer record (id is always included).
Null Handling
When a record has no matching relation (e.g., a sales order with no customer), the included relation is null:
{
id: 2,
tranid: 'SO-002',
entity: null, // No matching customer
}Supported Relation Types
| Relation Type | Example | Supported |
|---|---|---|
| Many-to-one | Sales order → Customer (via entity field) | Yes |
| One-to-many | Customer → Sales orders | Not yet |
One-to-many relations (reverse lookups) require a separate query or subquery and are planned for a future release. Only many-to-one include is supported today.
Finding Available Relations
Relations are defined in your schema. After running suiteportal introspect, check .suiteportal/schema.json to see what relations were discovered for each record:
{
"salesorder": {
"relations": {
"entity": {
"type": "many-to-one",
"target": "customer",
"foreignKey": "entity"
}
}
}
}The key name (e.g., entity) is what you use in include.
Combining with Other Options
include works alongside where, orderBy, take, and skip:
const orders = await ns.salesorder.findMany({
where: { tranid: { startsWith: 'SO' } },
select: { id: true, tranid: true },
include: { entity: true },
orderBy: { tranid: 'desc' },
take: 25,
});