Drizzle ORMRelationsError Fix2026 Updated

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

January 20267 min readDrizzle ORM · PostgreSQL · TypeScript

⚡ The Problem

Error
// 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

db/index.ts
import * as schema from "./schema"   // ← import all schema including relations
export const db = drizzle(pool, { schema })  // ← pass schema here

How 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.

1

Pass Schema to drizzle() When Using Relations

Root cause — most common fix
2 min
db/index.ts — correct setup
import { 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)
2

Define One-to-Many Relations Correctly

Post has many comments — user has many posts
5 min
db/schema.ts — one-to-many both sides required
import { 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),
})
3

Define Many-to-Many with a Junction Table

Post has many tags — tag has many posts
7 min
db/schema.ts — many-to-many pattern
export 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"
4

Use with: to Eagerly Load Related Data

Filter and limit related data in with:
3 min
Advanced with: options
// 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),
})
5

Use Joins as an Alternative to Relations

No relations() defined — use SQL join instead
4 min
db.select().leftJoin() — no relations() needed
import { 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 nested

Use 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

Frequently Asked Questions

Why does Drizzle relations return undefined for related data?+
The most common cause is not passing the schema to drizzle() when initialising the client. Drizzle needs the schema object (containing your table and relation definitions) to resolve relations. Without it, the with: clause silently returns undefined.
Do I need to define both sides of a relation in Drizzle?+
Yes. Unlike Prisma, Drizzle requires you to explicitly define both sides of a relation — the 'one' side and the 'many' side using relations(). Both are needed for the with: clause to work in either direction.
What is the difference between .references() and relations() in Drizzle?+
.references() is a SQL-level foreign key constraint — it tells the database that a column references another table's primary key. relations() is a Drizzle-level abstraction for querying related data using the with: clause. You typically need both: .references() for data integrity, relations() for convenient querying.
Can I query related data in Drizzle without using relations()?+
Yes — use SQL JOINs with db.select().from(table).leftJoin(otherTable, eq(table.id, otherTable.tableId)). This is more verbose but doesn't require relations() definitions. It is also more flexible for complex queries.
How do I do a many-to-many relation in Drizzle?+
Create a junction table with two foreign key columns referencing both tables. Define relations() on all three tables: the two main tables and the junction. Use with: { junctionTable: { with: { otherTable: true } } } to load the full graph.
Can Drizzle relations be used in Server Actions and API routes?+
Yes. The db client with relations is the same one you use everywhere — import it from your db file and use db.query.tableName.findMany({ with: { relation: true } }) in any server-side context.

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