drizzle-team / drizzle-team/drizzle-orm

[FEATURE]: DB Table Default Scopes

Open
#5,657 6 comments 3 reactions 0 assignees View on GitHub
enhancement
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Feature hasn't been suggested before.

- [x] I have verified this feature I'm about to request hasn't been suggested before.

### Describe the enhancement you want to request

In many applications, we need a consistent way to apply default filters to all queries on a table. Currently you have to create a function that returns the DB with these scopes applied. This is annoying as it can be easy to use the db directly and not the function getDb.

Common examples:

* Soft deletes (`deleted_at IS NULL`)
* Multi-tenant isolation (`organization_id = X`)
* Status filtering (e.g. only `active = true`)

Right now with Drizzle, this logic has to be manually added to every query or the DB has to be wrapped in a helper function, which:

* Is error-prone (easy to forget in one place)
* Leads to duplicated logic across the codebase
* Makes it harder to enforce invariants like tenant isolation

---

### Proposed Solution

Add support for **default scopes** (or global query filters) at the table level.

Example API (one possible direction):

```ts
const users = pgTable('users', {
id: serial('id').primaryKey(),
organizationId: integer('organization_id'),
deletedAt: timestamp('deleted_at'),
}).withDefaultScope((qb, ctx) =>
qb.where(
and(
isNull(users.deletedAt),
eq(users.organizationId, ctx.organizationId)
)
)
);
```

Then:

```ts
db.select().from(users)
```

Would automatically apply:

```sql
WHERE deleted_at IS NULL AND organization_id = ?
```

---

### Requirements / Considerations

* Ability to **override or disable** the default scope when needed:

```ts
db.select().from(users).unscoped()
```

* Ability to **compose scopes** (e.g. multiple conditions)

* Support for **context-aware scopes** (e.g. tenant ID passed via request context)

* Should work consistently across:

* `select`
* `update`
* `delete`

---

### Alternatives Considered

* Manually wrapping queries in helper functions → leads to fragmentation
* Custom repository layer → adds boilerplate and defeats Drizzle’s lightweight design

---

### Why this matters

This is a pretty standard feature in ORMs (e.g. Rails ActiveRecord default scopes), and it becomes essential for:

* Multi-tenant apps
* Soft delete patterns
* Enforcing global data constraints safely

---

Happy to help iterate on API design if this is something you’d consider adding.

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.