/** * Relational Queries with Drizzle ORM * * This file demonstrates how to query relations between tables using: * 1. Drizzle's relational query API (db.query) * 2. Manual JOINs */ import { drizzle } from 'drizzle-orm/d1'; import { users, posts, comments, usersRelations, postsRelations, commentsRelations } from './schema'; import { eq, desc, sql } from 'drizzle-orm'; /** * IMPORTANT: To use relational queries (db.query), you must pass the schema to drizzle() * * const db = drizzle(env.DB, { * schema: { users, posts, comments, usersRelations, postsRelations, commentsRelations } * }); */ /** * Relational Query API (db.query) * * This is the recommended way to query relations in Drizzle */ // Get user with all their posts export async function getUserWithPosts(db: ReturnType, userId: number) { return await db.query.users.findFirst({ where: eq(users.id, userId), with: { posts: true, }, }); } // Get user with posts and comments export async function getUserWithPostsAndComments(db: ReturnType, userId: number) { return await db.query.users.findFirst({ where: eq(users.id, userId), with: { posts: true, comments: true, }, }); } // Get post with author export async function getPostWithAuthor(db: ReturnType, postId: number) { return await db.query.posts.findFirst({ where: eq(posts.id, postId), with: { author: true, }, }); } // Get post with author and comments export async function getPostWithAuthorAndComments(db: ReturnType, postId: number) { return await db.query.posts.findFirst({ where: eq(posts.id, postId), with: { author: true, comments: { with: { author: true, // Nested: get comment author too }, }, }, }); } // Get all published posts with authors export async function getPublishedPostsWithAuthors(db: ReturnType, limit = 10) { return await db.query.posts.findMany({ where: eq(posts.published, true), with: { author: { columns: { id: true, name: true, email: true, // Exclude bio and timestamps }, }, }, orderBy: [desc(posts.createdAt)], limit, }); } // Get user with filtered posts (only published) export async function getUserWithPublishedPosts(db: ReturnType, userId: number) { return await db.query.users.findFirst({ where: eq(users.id, userId), with: { posts: { where: eq(posts.published, true), orderBy: [desc(posts.createdAt)], }, }, }); } // Get post with recent comments export async function getPostWithRecentComments( db: ReturnType, postId: number, commentLimit = 10 ) { return await db.query.posts.findFirst({ where: eq(posts.id, postId), with: { author: true, comments: { with: { author: true, }, orderBy: [desc(comments.createdAt)], limit: commentLimit, }, }, }); } /** * Manual JOINs * * For more complex queries, you can use manual JOINs */ // Left join: Get all users with their post counts export async function getUsersWithPostCounts(db: ReturnType) { return await db .select({ user: users, postCount: sql`count(${posts.id})`, }) .from(users) .leftJoin(posts, eq(posts.authorId, users.id)) .groupBy(users.id) .all(); } // Inner join: Get users who have posts export async function getUsersWithPosts(db: ReturnType) { return await db .select({ user: users, post: posts, }) .from(users) .innerJoin(posts, eq(posts.authorId, users.id)) .all(); } // Multiple joins: Get comments with post and author info export async function getCommentsWithDetails(db: ReturnType) { return await db .select({ comment: comments, post: { id: posts.id, title: posts.title, slug: posts.slug, }, author: { id: users.id, name: users.name, email: users.email, }, }) .from(comments) .innerJoin(posts, eq(comments.postId, posts.id)) .innerJoin(users, eq(comments.authorId, users.id)) .all(); } // Complex join with aggregation export async function getPostsWithCommentCounts(db: ReturnType) { return await db .select({ post: posts, author: users, commentCount: sql`count(${comments.id})`, }) .from(posts) .innerJoin(users, eq(posts.authorId, users.id)) .leftJoin(comments, eq(comments.postId, posts.id)) .groupBy(posts.id, users.id) .all(); } /** * Subqueries */ // Get users with more than 5 posts export async function getActiveAuthors(db: ReturnType) { const postCounts = db .select({ authorId: posts.authorId, count: sql`count(*)`.as('count'), }) .from(posts) .groupBy(posts.authorId) .as('post_counts'); return await db .select({ user: users, postCount: postCounts.count, }) .from(users) .innerJoin(postCounts, eq(users.id, postCounts.authorId)) .where(sql`${postCounts.count} > 5`) .all(); } /** * Aggregations with Relations */ // Get post statistics export async function getPostStatistics(db: ReturnType, postId: number) { const [stats] = await db .select({ post: posts, commentCount: sql`count(DISTINCT ${comments.id})`, }) .from(posts) .leftJoin(comments, eq(comments.postId, posts.id)) .where(eq(posts.id, postId)) .groupBy(posts.id) .all(); return stats; } /** * Tips for Relational Queries: * * 1. Use db.query for simple relations (cleaner syntax, type-safe) * 2. Use manual JOINs for complex queries with aggregations * 3. Use `with` to load nested relations * 4. Use `columns` to select specific fields * 5. Apply `where`, `orderBy`, `limit` within relations * 6. Remember: Must pass schema to drizzle() for db.query to work */ /** * Performance Tips: * * 1. Be selective with relations (only load what you need) * 2. Use `columns` to exclude unnecessary fields * 3. Apply limits to prevent loading too much data * 4. Consider pagination for large datasets * 5. Use indexes on foreign keys (already done in schema.ts) */ /** * Common Patterns: * * 1. One-to-Many: User has many Posts * - Use: db.query.users.findFirst({ with: { posts: true } }) * * 2. Many-to-One: Post belongs to User * - Use: db.query.posts.findFirst({ with: { author: true } }) * * 3. Nested Relations: Post with Author and Comments (with their Authors) * - Use: db.query.posts.findFirst({ * with: { * author: true, * comments: { with: { author: true } } * } * }) */