Skip to content

03 — JoinPlan

Status: Draft
Applies to: Compliance Level 1+

After parsing and validating a query, an implementation MUST produce a JoinPlan (logical name; not required to be a public JSON API). Resolvers consume this plan to fetch only requested data.

FieldTypeMeaning
rootEntitystringMeshQL entity key
fieldsstring[]Qualified scalar selections for the root (and possibly joins, depending on implementation). The TS planner uses table-prefixed paths such as users.id.
idFieldstringIdentifying field on the root entity (default id)
joinsResolvedJoin[]Nested relations requested by the client
readobject?Normalized selection and collection controls — see 05
contextobjectAt least: request correlation, HTTP method, optional entityId for point reads
FieldTypeMeaning
pathstringDot-separated ref path from root selection, e.g. comments or comments.author
joinKeystringSchema join key: {parentEntityKey}.{refName} e.g. post.comments or comment.author
entitystringTarget entity key
onstringJoin predicate / hint from schema (SQL-like string in the TS reference)
fieldsstring[]Selected fields for this join hop
type"one" | "many"Cardinality
refNamestringLast segment of path
idFieldstringIdentifying field of the joined entity (default id)

Query: post { id title comments { id body author { name } } }

Illustrative plan:

{
"rootEntity": "post",
"idField": "id",
"fields": ["posts.id", "posts.title"],
"joins": [
{
"path": "comments",
"joinKey": "post.comments",
"entity": "comment",
"type": "many",
"refName": "comments",
"idField": "id",
"on": "comments.post_id = posts.id",
"fields": ["comments.id", "comments.body"]
},
{
"path": "comments.author",
"joinKey": "comment.author",
"entity": "user",
"type": "one",
"refName": "author",
"idField": "id",
"on": "users.id = comments.author_id",
"fields": ["users.name"]
}
]
}

Exact string forms of fields / on may differ by SQL dialect adapters; cardinality, paths, and join keys MUST remain consistent with the query tree.

  • Point read: context.entityId is set.
  • Collection read: context.entityId is absent and read carries the normalized controls, including the effective page and deterministic order.

An implementation MAY skip the shaper if the resolver returns already-nested JSON matching the selection (TS: { preshaped: true }). That behavior is an optimization; the JoinPlan remains the source of truth for what was requested.