Relations
Define relationships between tables for type-safe queries and eager loading.
Defining Relations
schema.ts
import { defineSchema, got, stacked, simps, linked } from 'genaql';
const schema = defineSchema({
users: {
id: 'serial',
name: 'text',
email: 'text'
},
profiles: {
id: 'serial',
user_id: 'integer',
bio: 'text',
avatar_url: 'text'
},
posts: {
id: 'serial',
user_id: 'integer',
title: 'text',
content: 'text'
},
tags: {
id: 'serial',
name: 'text'
},
post_tags: {
post_id: 'integer',
tag_id: 'integer'
}
}, {
relations: {
users: {
profile: got('profiles', 'user_id'), // user got profile
posts: stacked('posts', 'user_id') // user stacked posts
},
profiles: {
user: simps('users', 'user_id') // profile simps for user
},
posts: {
author: simps('users', 'user_id'), // post simps for user
tags: linked('tags', 'post_tags', 'post_id', 'tag_id') // linked up
}
}
});Relation Types
| Classic | Gen Alpha | Description | Example |
|---|---|---|---|
hasOne() | got() | One-to-one | User has one Profile |
hasMany() | stacked() | One-to-many | User has many Posts |
belongsTo() | simps() | Inverse of hasOne/hasMany | Post belongs to User |
manyToMany() | linked() | Many-to-many via pivot | Posts linked to Tags |
Querying Relations
query-relations.ts
// Load user with their profile
const user = await db.query(
cook`main:users slay:* sus:id=1 fam:profile`
);
// { id: 1, name: "John", profile: { bio: "...", avatar_url: "..." } }
// Load user with all their posts
const userWithPosts = await db.query(
cook`main:users slay:* sus:id=1 fam:posts`
);
// { id: 1, name: "John", posts: [{ title: "...", content: "..." }, ...] }Nested Relations
nested.ts
// Load posts with author and tags
const posts = await db.query(
cook`main:posts slay:* fam:author,tags`
);
// [{ title: "...", author: { name: "John" }, tags: [{ name: "tech" }] }]
// Nested eager loading
const users = await db.query(
cook`main:users slay:* fam:posts.tags`
);
// [{ name: "John", posts: [{ title: "...", tags: [...] }] }]Type Safety
Relations are fully typed. TypeScript knows the shape of loaded relations and will error if you try to access a relation that wasn't eagerly loaded.