Skip to content

Relations

MeshQL relations live under schema.joins. Each key is {entityKey}.{refName} — always the entity key, never the plural selection path.

Query path (selection) Schema join key
{ blog { tags } } blog.tags
{ user { blogs { tags } } } still blog.tags (not blogs.tags)

Selection paths (blogs.tags) are used for SQL aliases and shaping. Schema keys stay on the entity.

"blog.author": {
entity: "user",
on: "blogs.author_id = users.id",
type: "one",
}
{ blog { id title author { name } } }
"user.blogs": {
entity: "blog",
on: "blogs.author_id = users.id",
type: "many",
}
{ user { id blogs { title } } }
"blog.tags": {
entity: "tag",
on: "blog_tags.blog_id = blogs.id",
type: "many",
through: {
table: "blog_tags",
from: "blog_id",
to: "tag_id",
},
},
"tag.blogs": {
entity: "blog",
on: "blog_tags.tag_id = tags.id",
type: "many",
through: {
table: "blog_tags",
from: "tag_id",
to: "blog_id",
},
}

Declare each direction you want to query. SQL builders emit parent → junction → child.

With user.blogs, blog.tags, and blog.author only:

{ user { blogs { title tags { name } author { name } } } }

MeshQL looks up nested refs via the parent entity (blog), so you do not declare blogs.tags or blogs.author.

"comment.replies": {
entity: "comment",
on: "comments.parent_id = comments.id",
type: "many",
}

The join key is still {entity}.{ref} (comment.replies). Nested paths use the ref name for aliases (replies.replies), but schema keys stay on comment.

Parent row holds a type discriminator + target id:

"comment.commentable": {
type: "one",
entities: ["post", "image"],
on: "/* derived from polymorphic */",
polymorphic: {
typeColumn: "commentable_type",
idColumn: "commentable_id",
map: { Post: "post", Image: "image" },
},
}
{
"comment": {
"$select": {
"id": true,
"commentable": { "$select": { "id": true, "title": true, "url": true } }
}
}
}

Selected fields may exist on any target. The response includes $entity:

{
"id": 1,
"commentable": { "$entity": "post", "id": 10, "title": "Hello" }
}

v1 limits: polymorphic type must be "one"; no through + polymorphic; no further nested joins under the polymorphic ref. Prefer SQL adapters (@meshql/sqlite / @meshql/postgres); ORM include trees may need a custom / preshaped resolver.

  • Do not declare blogs.tags for nesting under user.blogs — use blog.tags.
  • Do not use SQL table names as join-key parents (users.tokens); use entity keys (user.tokens).
  • Pointing entity.table at a database view is fine; there is no separate view join type.

See also Concepts and the JoinPlan spec.