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

ClassicGen AlphaDescriptionExample
hasOne()got()One-to-oneUser has one Profile
hasMany()stacked()One-to-manyUser has many Posts
belongsTo()simps()Inverse of hasOne/hasManyPost belongs to User
manyToMany()linked()Many-to-many via pivotPosts 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.