@dudousxd/nestjs-filter-drizzle

中文文档
0.3.0发布于 2026/11/12MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2101

Drizzle ORM adapter for @dudousxd/nestjs-filter.

原始使用文档
文档目录12 节
  1. Install
  2. Quick Start
  3. How it maps onto Drizzle
  4. DrizzleQuery API
  5. Filters that double as relation filters
  6. Computed fields
  7. Testing
  8. Limitations
  9. API Reference
  10. DrizzleFilter<TTable>
  11. DrizzleFilterModule.forRoot({ connection | db, schema?, dia…
  12. DrizzleAdapter

@dudousxd/nestjs-filter-drizzle

Drizzle ORM adapter for @dudousxd/nestjs-filter.

Provides DrizzleFilter, DrizzleAdapter, DrizzleQuery, and DrizzleFilterModule. Postgres, MySQL and SQLite are supported (every dialect runs the cross-adapter contract suite).

Install

pnpm add @dudousxd/nestjs-filter @dudousxd/nestjs-filter-drizzle

Peer dependencies: drizzle-orm >= 0.40 < 1.0, @nestjs/common >= 10, @nestjs/core >= 10. Optional: @nestjs/drizzle >= 0.0.2 < 1.0, whose database the adapter uses by default.

Quick Start

// schema.ts — the table object IS the entity
import { relations } from 'drizzle-orm';
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  age: integer('age').notNull(),
  role: text('role').notNull(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  status: text('status').notNull(),
  authorId: integer('author_id').notNull(),
});

// relations() powers includes, dot-notation filters, whereHas(), aggregates and describe()
export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));
export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));

export const schema = { users, posts, usersRelations, postsRelations };
// user.filter.ts
import { Injectable } from '@nestjs/common';
import { Filterable, FilterFor } from '@dudousxd/nestjs-filter';
import { DrizzleFilter } from '@dudousxd/nestjs-filter-drizzle';
import { eq, gte } from 'drizzle-orm';
import { posts, users } from './schema.js';

@Injectable()
@Filterable({ entity: users })
export class UserFilter extends DrizzleFilter<typeof users> {
  static readonly sort = ['name', 'age', 'posts.$count'];
  static readonly search = ['name'];
  static readonly includes = ['posts'];

  @FilterFor('minAge')
  applyMinAge(value: number) {
    this.$query.where(gte(users.age, value));
  }

  @FilterFor('name')
  applyName(value: string) {
    this.whereILike('name', value); // escaped; ILIKE on Postgres, lower() LIKE elsewhere
  }

  @FilterFor('hasPublished')
  applyHasPublished(value: boolean) {
    if (value) this.$query.whereHas('posts', (p) => eq((p as typeof posts).status, 'published'));
  }
}
// app.module.ts — the database comes from NestJS's @nestjs/drizzle (https://docs.nestjs.com/data/drizzle)
import { Module } from '@nestjs/common';
import { DrizzleModule } from '@nestjs/drizzle';
import { FilterModule } from '@dudousxd/nestjs-filter';
import { drizzleAdapter } from '@dudousxd/nestjs-filter-drizzle';
import { drizzle } from 'drizzle-orm/node-postgres';
import { schema } from './schema.js';

@Module({
  imports: [
    DrizzleModule.forRoot({ drizzle, connection: process.env.DATABASE_URL!, schema }),
    // no `connection`/`db`: injects getDrizzleToken(name) from @nestjs/drizzle
    FilterModule.forRoot({ inputNormalizer: 'camelCase', adapter: drizzleAdapter() }),
    FilterModule.forFeature([UserFilter]),
  ],
  controllers: [UsersController],
})
export class AppModule {}

Named connection: drizzleAdapter({ name: 'reports' }) for DrizzleModule.forRoot({ name: 'reports', ... }). @nestjs/drizzle is an optional peer: without it, pass your own token (drizzleAdapter({ connection: DRIZZLE }), provided from a @Global() module) or the instance (drizzleAdapter({ db })).

// users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ApplyFilter } from '@dudousxd/nestjs-filter';
import type { DrizzleQuery } from '@dudousxd/nestjs-filter-drizzle';

@Controller('users')
export class UsersController {
  @Get()
  list(@ApplyFilter(UserFilter) q: DrizzleQuery<typeof users>) {
    return q.execute(); // or q.executeAndCount(), q.count(), q.toSelect(), q.toSQL()
  }
}

GET /users?minAge=18&name=al&sort=-age&include=posts&paginate[page]=0&paginate[size]=20

How it maps onto Drizzle

ConceptMikroORM / TypeORM adaptersDrizzle adapter
Entitydecorated class (User)table object (users)
MetadataORM metadata registrygetTableColumns(table) + the relations() in your schema
this.$querythe ORM's query builderDrizzleQuery — an accumulator of SQL conditions, ordering, window, projection and includes, materialized as ONE db.select().from(table) on execute
Relation filter (posts.status, @Relations, related())JOINcorrelated EXISTS (…) subquery
Sort / distinct on a to-one path (manager.name)JOINcorrelated scalar subquery
includejoin / ORM populatebatched SELECT … WHERE fk IN (…) per relation, grafted as row.posts[] / row.author (the shape db.query…findMany({ with }) returns)
findAndCount totalORM countcount(*) over the same WHERE (a derived table for distinct)
Aggregates (posts.$count)correlated subquerycorrelated subquery

The root query never joins, so it always returns one row per matching parent: LIMIT/OFFSET and COUNT(*) stay exact whatever relations a request touches.

DrizzleQuery API

Member
where(...conds)AND SQL conditions (undefined ignored — compose and()/or() freely)
andWhere(cond | { col: value })also accepts an equality map (array → IN, null → IS NULL)
whereHas(path, (target) => cond?) / whereDoesntHave(...)correlated EXISTS over a relation path ('posts.comments')
orderBy(...terms), clearOrderBy(), limit(n), offset(n)ordering and window
select(fields), addSelect(fields), distinct()projection
include(...paths)relations loaded by execute()
columns, table, db, dialectthe (possibly aliased) table's columns, and the execution context
execute(), count(), executeAndCount()run it
toSelect(), toSQL()the materialized drizzle builder / SQL + params

Filters that double as relation filters

When a filter runs through @Relations (or this.related(...)), its $query targets an alias of its table inside the EXISTS subquery. Read columns through this.columns (or the callback argument of whereHas) rather than the imported table object in such filters:

@FilterFor('postStatus')
applyStatus(value: string) {
  this.$query.where(eq(this.columns.status, value));
}

Computed fields

Same three source forms as the other adapters. Because the adapter never joins, an unqualified column in a string source always means the root table's column; a function source receives { alias: <table name>, em: db } and may return a SQL string, a drizzle sql fragment, or a select builder (used as a scalar subquery):

@Filterable({
  entity: users,
  computed: {
    doubleAge: '(age * 2)',
    label: { source: `name || ' <' || role || '>'`, project: true },
  },
})
export class UserFilter extends DrizzleFilter<typeof users> {
  @Computed({ type: 'number' })
  postCount() {
    return sql`(select count(*) from ${posts} where ${posts.authorId} = ${users.id})`;
  }
}

Testing

No database needed: run a filter against a DrizzleQuery over drizzle.mock() and assert the SQL.

import { drizzle } from 'drizzle-orm/node-postgres';

const q = new DrizzleAdapter(drizzle.mock()).query(users);
await runner.apply(UserFilter, { name: 'al', minAge: 18 }, q);
expect(q.toSQL()).toEqual({
  sql: 'select "id", "name", "age" from "users" where ("users"."name" ilike $1 and "users"."age" >= $2)',
  params: ['%al%', 18],
});

FilterTestingModule + makeMockQueryBuilder also work: the mock records ['where', SQL] calls, which you can render with the dialect (new PgDialect().sqlToQuery(sql)).

Limitations

  • drizzle-orm 0.x only (relations() v1). Drizzle 1.0's defineRelations is not read yet.
  • No many-to-many relation kind: drizzle models it as a junction table, so it shows up as a one-to-many to the junction (user.userTags[].tag).
  • JSON sub-paths (metadata.tier) are not resolved (the MikroORM adapter supports them).
  • Composite-key relations are skipped by include; filtering through them works.
  • Vector search (search = { vector }) is Postgres-only.
  • Codegen (@dudousxd/nestjs-filter-codegen) reads entity classes via the TypeScript AST; it does not yet derive filterFields from Drizzle tables.

API Reference

DrizzleFilter<TTable>

Abstract base class. this.$query is a DrizzleQuery<TTable>. Helpers: columns, whereLike, whereILike, whereBeginsWith, whereEndsWith (all escape the value).

DrizzleFilterModule.forRoot({ connection | db, schema?, dialect?, imports? })

Registers DrizzleAdapter as the filter adapter. connection is the injection token of your drizzle instance (provided globally, or by a module listed in imports); db passes the instance directly. schema defaults to the schema the instance was created with. drizzleAdapter(options) is the same thing as a descriptor for FilterModule.forRoot({ adapter }).

DrizzleAdapter

Implements FilterAdapter. adapter.query(table) creates a DrizzleQuery for services:

const q = adapter.query(users);
await runner.apply(UserFilter, input, q);
const { rows, total } = await q.executeAndCount();

版本与发布记录

@dudousxd/nestjs-filter-drizzle 共发布 8 个版本,首次发布于 2026/09/29;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.0latest2026/11/122 天前
0.2.12026/11/095 天前
0.2.02026/11/086 天前
0.1.32026/11/0212 天前
1.0.02026/11/0112 天前
0.1.22026/10/2718 天前

关于 @dudousxd/nestjs-filter-drizzle 的常见问题

@dudousxd/nestjs-filter-drizzle 是什么?

Drizzle ORM adapter for @dudousxd/nestjs-filter.

如何安装 @dudousxd/nestjs-filter-drizzle?

在项目目录运行 npm install @dudousxd/nestjs-filter-drizzle,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:drizzle-orm(>=0.40.0 <1.0.0)、@nestjs/core(>=10.0.0)、@nestjs/common(>=10.0.0)、reflect-metadata(>=0.1.13)、@dudousxd/nestjs-filter(^2.0.1)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@dudousxd/nestjs-filter-drizzle 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dudousxd/nestjs-filter-drizzle 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dudousxd/nestjs-filter-drizzle 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@dudousxd/nestjs-filter-drizzle 可以商用吗?

@dudousxd/nestjs-filter-drizzle 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dudousxd/nestjs-filter-drizzle 的下载量有多少?

根据 npm 官方下载统计,@dudousxd/nestjs-filter-drizzle 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,101 次,日均约 70 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dudousxd/nestjs-filter-drizzle 还在维护吗?

最新版本 0.3.0 发布于 2026/11/12(2 天前);至今共发布 8 个版本,其中过去 12 个月发布了 8 个;首次发布于 2026/09/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。