Drizzle ORM Relations Not Working— with: Clause Fix [2026]
Also covers: pass schema to drizzle() · one-to-many · many-to-many · with: eager loading · join alternative
⚡ The Problem
// with: clause returns undefined instead of related data
const posts = await db.query.posts.findMany({
with: { author: true },
})
console.log(posts[0].author) // undefined✅ Root cause — missing schema in drizzle() init
import * as schema from "./schema" // ← import all schema including relations
export const db = drizzle(pool, { schema }) // ← pass schema hereHow Drizzle Relations Work
Drizzle has two query APIs: the select API (SQL-like) and the query API (relational, like Prisma). The query API's with: clause requires: 1) relations defined using relations() in your schema, and 2) the schema passed to drizzle() when creating the client.
Pass Schema to drizzle() When Using Relations
Root cause — most common fiximport { drizzle } from "drizzle-orm/node-postgres"
import { Pool } from "pg"
import * as schema from "./schema" // ← import everything including relations
const pool = new Pool({ connectionString: process.env.DATABASE_URL! })
// ✅ Pass schema — required for db.query.* and with: to work
export const db = drizzle(pool, { schema })
// ❌ Missing schema — with: returns undefined silently
// export const db = drizzle(pool)Define One-to-Many Relations Correctly
Post has many comments — user has many postsimport { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core"
import { relations } from "drizzle-orm"
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
})
export const posts = pgTable("posts", {
id: uuid("id").primaryKey().defaultRandom(),
title: text("title").notNull(),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
createdAt: timestamp("created_at").notNull().defaultNow(),
})
// ✅ Define relations on BOTH sides
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts), // user has many posts
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.userId], // the FK column in posts
references: [users.id], // the PK column in users
}),
}))
// Usage:
const usersWithPosts = await db.query.users.findMany({
with: { posts: true }, // loads all posts for each user
})
const postWithAuthor = await db.query.posts.findFirst({
with: { author: true }, // loads the user for the post
where: eq(posts.id, postId),
})Define Many-to-Many with a Junction Table
Post has many tags — tag has many postsexport const tags = pgTable("tags", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull().unique(),
})
// Junction table — the bridge between posts and tags
export const postsToTags = pgTable("posts_to_tags", {
postId: uuid("post_id").notNull().references(() => posts.id, { onDelete: "cascade" }),
tagId: uuid("tag_id").notNull().references(() => tags.id, { onDelete: "cascade" }),
})
// Relations on all 3 tables
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.userId], references: [users.id] }),
postTags: many(postsToTags),
}))
export const tagsRelations = relations(tags, ({ many }) => ({
postTags: many(postsToTags),
}))
export const postsToTagsRelations = relations(postsToTags, ({ one }) => ({
post: one(posts, { fields: [postsToTags.postId], references: [posts.id] }),
tag: one(tags, { fields: [postsToTags.tagId], references: [tags.id] }),
}))
// Query — posts with their tags
const posts = await db.query.posts.findMany({
with: {
postTags: {
with: { tag: true }, // load tag data through the junction
},
},
})
// posts[0].postTags[0].tag.name → "TypeScript"Use with: to Eagerly Load Related Data
Filter and limit related data in with:// Load posts with only published comments, limited to 5
const posts = await db.query.posts.findMany({
with: {
comments: {
where: eq(comments.published, true),
limit: 5,
orderBy: desc(comments.createdAt),
with: {
author: {
columns: { id: true, name: true }, // select only specific columns
},
},
},
author: {
columns: { id: true, name: true, email: true },
},
},
where: eq(posts.published, true),
limit: 10,
orderBy: desc(posts.createdAt),
})Use Joins as an Alternative to Relations
No relations() defined — use SQL join insteadimport { eq, desc } from "drizzle-orm"
// Left join — get posts with author name
const postsWithAuthors = await db
.select({
postId: posts.id,
postTitle: posts.title,
authorName: users.name,
authorEmail: users.email,
})
.from(posts)
.leftJoin(users, eq(posts.userId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt))
.limit(10)
// Result is flat: { postId, postTitle, authorName, authorEmail }[]
// Unlike with:, joins return a flat object — not nestedUse the query API (db.query) with with: for most cases — it returns nested objects and is easier to type. Use the select API (.select().from().leftJoin()) for complex aggregations, custom column selection, and performance-sensitive queries.
Prevention
- Always pass { schema } to drizzle() — without it, db.query.* and with: silently return undefined
- Import all schema including relation definitions with import * as schema from './schema'
- Define relations on BOTH sides of every relationship — Drizzle requires explicit bidirectional definitions
- Use .references() for FK constraints AND relations() for query-level loading — both are needed
- For many-to-many, always create a junction table and define relations on all 3 tables
- Test relations in drizzle-kit studio — it shows your schema graph visually
Frequently Asked Questions
Why does Drizzle relations return undefined for related data?+−
Do I need to define both sides of a relation in Drizzle?+−
What is the difference between .references() and relations() in Drizzle?+−
Can I query related data in Drizzle without using relations()?+−
How do I do a many-to-many relation in Drizzle?+−
Can Drizzle relations be used in Server Actions and API routes?+−
Need Expert Help?
We Build Type-Safe Apps with Drizzle ORM
Softplix engineers design Drizzle schemas with relations, type-safe queries, and performant joins for production applications. Let us help.
Talk to an Engineer