drizzle-team / drizzle-team/drizzle-orm

[BUG]: drizzle-kit pull generates many() instead of one() for one-to-one relations (unique FK columns)

Open
#5,478 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Report hasn't been filed before.

- [x] I have verified that the bug I'm about to report hasn't been filed before.

### What version of `drizzle-orm` are you using?

1.0.0-beta.16

### What version of `drizzle-kit` are you using?

1.0.0-beta.16

### Other packages

_No response_

### Describe the Bug

**What is the undesired behavior?**

`drizzle-kit pull` generates `many()` for the reverse side of a one-to-one relationship. When a foreign key column has a `UNIQUE` constraint, the reverse relation should be `one()`, not `many()`.

**Steps to reproduce:**

Given this PostgreSQL schema:

```sql
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid()
);

CREATE TABLE profiles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid UNIQUE NOT NULL REFERENCES users(id)
);
```

`user_id` is `UNIQUE`, so `users → profiles` is a one-to-one relationship.

Run `drizzle-kit pull`.

**Actual result:**

```typescript
export const usersRelations = relations(users, ({ many }) => ({
profiles: many(profiles), // incorrect — should be one()
}));

export const profilesRelations = relations(profiles, ({ one }) => ({
user: one(users, { fields: [profiles.userId], references: [users.id] }),
}));
```

**Desired result:**

```typescript
export const usersRelations = relations(users, ({ one }) => ({
profile: one(profiles), // one-to-one reverse
}));

export const profilesRelations = relations(profiles, ({ one }) => ({
user: one(users, { fields: [profiles.userId], references: [users.id] }),
}));
```

**Root cause:**

In `relationsToTypeScript`, the code checks `table.uniques` for a matching constraint on `columnsFrom`, but the comparison uses the **cased column names** (post-`withCasing`) against the raw unique constraint column names. When casing is applied (e.g., `camelCase` mode), `columnsFrom` becomes `["userId"]` while the unique constraint stores `["user_id"]`, so the `.every((col, i) => col === columnsFrom[i])` check always fails.

**Database:** PostgreSQL 18.3

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.