Skip to content

Conversation

@jtomaszewski
Copy link
Contributor

@jtomaszewski jtomaszewski commented Dec 8, 2025

Battle-tested in one of my apps for the last few months. It may not be perfect yet, but it is a good start I think.

Summary

  • Add @ptc-org/nestjs-query-mikroorm package that provides MikroORM integration
  • Implements MikroOrmQueryService following the QueryService interface from @ptc-org/nestjs-query-core
  • Provides NestjsQueryMikroOrmModule.forFeature() for easy module registration
  • Comprehensive test suite with 62 tests covering all operations

Test plan

  • FilterQueryBuilder converts all filter operations correctly
  • MikroOrmQueryService implements query, count, findById, getById
  • MikroOrmQueryService implements createOne, createMany
  • MikroOrmQueryService implements updateOne, updateMany
  • MikroOrmQueryService implements deleteOne, deleteMany
  • MikroOrmQueryService implements relation queries
  • NestjsQueryMikroOrmModule.forFeature() creates providers correctly

Summary by CodeRabbit

Release Notes

  • New Features

    • Added MikroORM database integration support for query service operations, including filtering, sorting, paging, and relation querying.
    • Introduced automated preview publishing workflow for pull requests.
  • Chores

    • Added new package structure and configuration for MikroORM integration.
    • Updated dependencies with required database libraries.
  • Tests

    • Added comprehensive test suites for MikroORM query service functionality and module integration.

✏️ Tip: You can customize this high-level summary in your review settings.

jtomaszewski and others added 8 commits December 8, 2025 09:07
Enable preview package releases on pull requests and workflow dispatch
using pkg.pr.new. This allows testing packages before official release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
feat(ci): add pkg.pr.new preview releases
Add @ptc-org/nestjs-query-mikro-orm package providing MikroORM integration
for nestjs-query. This includes:

- MikroOrmQueryService implementing QueryService interface
- Filter conversion from nestjs-query to MikroORM operators
- Support for sorting with null handling
- Relation queries (findRelation, queryRelations, countRelations)
- NestjsQueryMikroOrmModule with forFeature() pattern
- Factory providers for automatic service registration
- MikroOrmAssembler for DTO/Entity conversion
- Comprehensive test suite with 46 tests

Closes TriPSs#178

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add missing .eslintrc.json for package
- Move function definition before usage to fix no-use-before-define
- Prefix unused parameters with underscore
- Auto-fix formatting issues from prettier/eslint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The better-sqlite3 native module needs to be compiled per Node version.
Previous cache key only used yarn.lock hash, causing NODE_MODULE_VERSION
mismatch errors when running tests on Node 22.x and 23.x.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…a5f9d7)

throw an error if something is passed that we don't support, e.g. filtering

(are there any other options that we silently ignore?)
…n (vibe-kanban c60da658)

## Problem

When using `@Authorize` decorator with a `CustomAuthorizer` that returns an empty filter `{}`, the `findRelationForEntity` method throws:

```
Error: MikroOrmQueryService does not support filtering on findRelation
```

This happens because the check `if (opts?.filter)` is truthy for empty objects `{}`.

## Root Cause

In `packages/query-mikro-orm/src/services/mikro-orm-query.service.ts`, line ~187:

```typescript
if (opts?.filter) {
  throw new Error('MikroOrmQueryService does not support filtering on findRelation')
}
```

An empty filter `{}` is truthy, so it triggers the error even though there are no actual filter conditions.

## Solution

Change the check to verify the filter has actual conditions:

```typescript
if (opts?.filter && Object.keys(opts.filter).length > 0) {
  throw new Error('MikroOrmQueryService does not support filtering on findRelation')
}
```

## Related

This affects any DTO using `@Authorize` with a `CustomAuthorizer` that returns `{}` when accessing `@Relation` fields.
@coderabbitai
Copy link

coderabbitai bot commented Dec 8, 2025

Walkthrough

This PR introduces a new Mikro-ORM integration package (@ptc-org/nestjs-query-mikro-orm) for nestjs-query with complete test fixtures, a query service supporting CRUD and relation operations, NestJS module setup, provider wiring, and build configuration. It adds Mikro-ORM dependencies to the root package and updates GitHub Actions workflows to support preview publishing.

Changes

Cohort / File(s) Summary
GitHub Actions Setup & Workflows
.github/actions/setup-step/action.yml, .github/workflows/publish-preview.yml
Introduces Node version capture and immutable dependency installation in setup action; adds new "Publish Preview" workflow triggering on PRs and manual dispatch with environment variables, build steps, and preview publishing via pkg-pr-new.
Root Dependencies
package.json
Adds three Mikro-ORM devDependencies: @mikro-orm/core, @mikro-orm/better-sqlite, and @mikro-orm/nestjs (all ^6.x versions).
Query-MikroORM Package Configuration
packages/query-mikro-orm/package.json, packages/query-mikro-orm/project.json, packages/query-mikro-orm/.eslintrc.json
Establishes new package metadata, Nx project targets (build, lint, test, version, publish), and ESLint configuration extending root rules.
TypeScript Configuration
packages/query-mikro-orm/tsconfig.json, packages/query-mikro-orm/tsconfig.lib.json, packages/query-mikro-orm/tsconfig.spec.json
Defines TypeScript project structure with lib and spec configurations, CommonJS output, and comprehensive test file patterns.
Jest Configuration
packages/query-mikro-orm/jest.config.ts
Configures Jest test runner with ts-jest transformer, Node environment, and coverage output directory.
Test Fixtures & Entities
packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts, packages/query-mikro-orm/__tests__/__fixtures__/test-relation.entity.ts, packages/query-mikro-orm/__tests__/__fixtures__/connection.fixture.ts, packages/query-mikro-orm/__tests__/__fixtures__/seeds.ts, packages/query-mikro-orm/__tests__/__fixtures__/index.ts
Defines TestEntity and TestRelation Mikro-ORM entities with various relation types (OneToMany, ManyToOne, ManyToMany, OneToOne); provides connection setup, schema creation, truncation, refresh, and seeding utilities.
Test Suites
packages/query-mikro-orm/__tests__/module.spec.ts, packages/query-mikro-orm/__tests__/providers.spec.ts, packages/query-mikro-orm/__tests__/services/mikro-orm-query.service.spec.ts
Adds integration tests for module initialization, provider generation with optional DTO/assembler mappings, and comprehensive service tests covering filters, relations, paging, sorting, and null handling.
Assembler Implementation
packages/query-mikro-orm/src/assemblers/mikro-orm.assembler.ts, packages/query-mikro-orm/src/assemblers/index.ts
Implements MikroOrmAssembler with identity-based type casting between DTOs, entities, and query/aggregate structures.
Query Service Implementation
packages/query-mikro-orm/src/services/mikro-orm-query.service.ts, packages/query-mikro-orm/src/services/index.ts
Provides MikroOrmQueryService with getById, findById, query, findRelation, countRelations, and queryRelations methods; includes filter translation (eq, neq, gt, gte, lt, lte, in, notIn, like, ilike), sorting with null handling, and optional DTO-entity assembler support.
NestJS Module & Providers
packages/query-mikro-orm/src/providers.ts, packages/query-mikro-orm/src/module.ts
Defines EntityServiceOptions interface; implements createMikroOrmQueryServiceProvider(s) for optional DTO/Assembler wiring; exports NestjsQueryMikroOrmModule with forFeature static method for provider registration.
Public Barrel Export
packages/query-mikro-orm/src/index.ts
Re-exports MikroOrmAssembler, NestjsQueryMikroOrmModule, query service providers, and MikroOrmQueryService.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • MikroOrmQueryService implementation: Dense logic for filter translation, relation querying, and DTO/entity conversion with multiple overloads and private helper methods requiring close inspection
  • Provider wiring complexity: EntityServiceOptions, factory providers, and dependency injection setup with optional assembler resolution paths
  • Test coverage scope: 40+ spec tests across fixtures, module, providers, and comprehensive service tests covering edge cases (null handling, combined filters, relation operations)
  • Heterogeneous file spread: Mix of configuration, entity definitions, test infrastructure, and implementation code across distinct concerns
  • TypeScript generics: Multiple generic parameter chains and type constraints throughout providers, service, and assembler requiring careful type safety review

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(query-mikro-orm): add MikroORM adapter package' accurately and clearly summarizes the main change in the PR—the addition of a new MikroORM adapter package.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Dec 8, 2025

View your CI Pipeline Execution ↗ for commit e7e70b6

Command Status Duration Result
nx run-many --target=e2e --all ✅ Succeeded <1s View ↗
nx run-many --target=test --all ✅ Succeeded <1s View ↗
nx run-many --target=lint --all ✅ Succeeded 1s View ↗
nx run-many --target=build --all ✅ Succeeded 1s View ↗
nx run workspace:version ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-08 13:46:14 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
packages/query-mikro-orm/.eslintrc.json (1)

11-35: Consider simplifying redundant override blocks.

The three override blocks all have empty rules, and the first block (lines 12-20) already covers all file types that the second and third blocks target. The additional blocks add no value.

Apply this diff to simplify:

   "overrides": [
     {
       "files": [
         "*.ts",
         "*.tsx",
         "*.js",
         "*.jsx"
       ],
       "rules": {}
-    },
-    {
-      "files": [
-        "*.ts",
-        "*.tsx"
-      ],
-      "rules": {}
-    },
-    {
-      "files": [
-        "*.js",
-        "*.jsx"
-      ],
-      "rules": {}
     }
   ]
package.json (1)

49-51: Consider upgrading MikroORM packages to their latest stable versions.

The specified versions (6.4.0 and 6.1.0) have no known security vulnerabilities. However, they are outdated: @mikro-orm/core and @mikro-orm/better-sqlite should be upgraded to 6.6.1, and @mikro-orm/nestjs to 6.1.1. These versions are compatible with each other (all 6.x), though upgrading from 6.1.0 to 6.4.0+ includes minor API changes (e.g., tsNode option renamed to preferTs) that may require adjustments.

packages/query-mikro-orm/src/assemblers/mikro-orm.assembler.ts (1)

3-37: Consider adding documentation for identity casting approach.

The assembler performs identity casts between DTO and Entity types, assuming they have compatible structures. While this is a reasonable default for MikroORM where entities can serve directly as DTOs, consider adding a class-level comment explaining this behavior and that users should provide custom assemblers when transformation is needed.

+/**
+ * Default assembler for MikroORM that performs identity casts between DTO and Entity.
+ * Assumes DTO and Entity have compatible structures. For custom transformations,
+ * provide a custom assembler class via EntityServiceOptions.
+ */
 export class MikroOrmAssembler<
packages/query-mikro-orm/src/services/mikro-orm-query.service.ts (1)

293-304: countRelationsForEntity assumes relation is a Collection.

Line 300 casts the relation to Collection<RelationEntity>, which will fail at runtime if called with a single-valued relation (Reference). While counting typically applies to collections, consider adding a runtime check or type guard.

+  if (!(collection instanceof Collection)) {
+    throw new Error(`Relation '${relationName}' is not a collection`)
+  }
   const count = await collection.loadCount({ where })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c5a767 and e7e70b6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (25)
  • .github/actions/setup-step/action.yml (1 hunks)
  • .github/workflows/publish-preview.yml (1 hunks)
  • package.json (1 hunks)
  • packages/query-mikro-orm/.eslintrc.json (1 hunks)
  • packages/query-mikro-orm/__tests__/__fixtures__/connection.fixture.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/__fixtures__/index.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/__fixtures__/seeds.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/__fixtures__/test-relation.entity.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/module.spec.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/providers.spec.ts (1 hunks)
  • packages/query-mikro-orm/__tests__/services/mikro-orm-query.service.spec.ts (1 hunks)
  • packages/query-mikro-orm/jest.config.ts (1 hunks)
  • packages/query-mikro-orm/package.json (1 hunks)
  • packages/query-mikro-orm/project.json (1 hunks)
  • packages/query-mikro-orm/src/assemblers/index.ts (1 hunks)
  • packages/query-mikro-orm/src/assemblers/mikro-orm.assembler.ts (1 hunks)
  • packages/query-mikro-orm/src/index.ts (1 hunks)
  • packages/query-mikro-orm/src/module.ts (1 hunks)
  • packages/query-mikro-orm/src/providers.ts (1 hunks)
  • packages/query-mikro-orm/src/services/index.ts (1 hunks)
  • packages/query-mikro-orm/src/services/mikro-orm-query.service.ts (1 hunks)
  • packages/query-mikro-orm/tsconfig.json (1 hunks)
  • packages/query-mikro-orm/tsconfig.lib.json (1 hunks)
  • packages/query-mikro-orm/tsconfig.spec.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/query-mikro-orm/__tests__/__fixtures__/seeds.ts (1)
packages/query-typegoose/__tests__/__fixtures__/test.entity.ts (1)
  • TestEntity (6-39)
packages/query-mikro-orm/src/assemblers/mikro-orm.assembler.ts (5)
packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts (1)
  • Entity (5-33)
packages/query-mikro-orm/src/services/mikro-orm-query.service.ts (1)
  • query (57-73)
packages/core/src/interfaces/query.interface.ts (1)
  • Query (30-39)
packages/core/src/interfaces/aggregate-query.interface.ts (1)
  • AggregateQuery (20-27)
packages/core/src/interfaces/aggregate-response.interface.ts (1)
  • AggregateResponse (9-16)
packages/query-mikro-orm/__tests__/__fixtures__/test-relation.entity.ts (1)
packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts (1)
  • Entity (5-33)
packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts (1)
packages/query-mikro-orm/__tests__/__fixtures__/test-relation.entity.ts (1)
  • Entity (5-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: check (23.x, lint)
  • GitHub Check: check (23.x, test)
  • GitHub Check: check (22.x, build)
  • GitHub Check: check (22.x, lint)
  • GitHub Check: check (23.x, build)
  • GitHub Check: e2e-test (21.x, mysql)
  • GitHub Check: e2e-test (20.x, mysql)
  • GitHub Check: check (22.x, test)
  • GitHub Check: e2e-test (20.x, postgres)
  • GitHub Check: e2e-test (21.x, postgres)
  • GitHub Check: publish-preview
  • GitHub Check: Analyze
  • GitHub Check: federation-e2e-test
🔇 Additional comments (29)
packages/query-mikro-orm/src/services/index.ts (1)

1-1: LGTM!

Clean barrel export for the service module.

packages/query-mikro-orm/src/assemblers/index.ts (1)

1-1: LGTM!

Clean barrel export for the assembler module.

packages/query-mikro-orm/tsconfig.json (1)

1-13: LGTM!

Standard TypeScript project configuration with proper project references for library and spec builds.

packages/query-mikro-orm/tsconfig.lib.json (1)

1-20: LGTM!

Proper TypeScript library configuration with appropriate test exclusions and CommonJS output.

packages/query-mikro-orm/__tests__/__fixtures__/index.ts (1)

1-4: LGTM!

Clean barrel export consolidating test fixtures for convenient imports.

.github/actions/setup-step/action.yml (3)

8-11: LGTM! Good cache busting strategy.

Capturing the Node version for use in the cache key ensures cache invalidation when the Node version changes, preventing stale dependency issues.


20-20: LGTM! Improved cache key.

Including both the Node version and yarn.lock hash in the cache key provides robust cache invalidation.


22-25: LGTM! Conditional install is correct.

The conditional dependency installation only runs on cache miss, optimizing CI performance.

packages/query-mikro-orm/package.json (1)

1-42: LGTM!

The package configuration is well-structured with appropriate peer dependencies for flexibility and specific dev dependencies for testing. The separation of concerns between runtime peer dependencies (^6.0.0 range) and test dependencies (^6.4.0 specific) is correct.

packages/query-mikro-orm/tsconfig.spec.json (1)

1-23: LGTM!

The test TypeScript configuration is standard and comprehensive, with appropriate module format (commonjs) for Jest and comprehensive test file patterns.

.github/workflows/publish-preview.yml (1)

1-31: LGTM!

The preview publishing workflow is well-structured with proper checkout depth for Nx caching and uses Node.js 22.x. The workflow appropriately builds all targets before publishing previews.

packages/query-mikro-orm/src/index.ts (1)

1-4: LGTM!

Clean barrel export file that properly exposes the public API of the MikroORM integration package.

packages/query-mikro-orm/__tests__/providers.spec.ts (1)

1-49: LGTM!

Comprehensive test coverage for provider creation, including entity classes, custom DTOs, repository token injection with optional data source, and factory instantiation. The test structure is clear and well-organized.

packages/query-mikro-orm/__tests__/services/mikro-orm-query.service.spec.ts (1)

1-360: LGTM!

Excellent comprehensive test suite with thorough coverage of all query service operations including:

  • All filter operators (eq, neq, gt, gte, lt, lte, in, notIn, like, is, isNot)
  • AND/OR filter combinations
  • Paging and sorting with null handling
  • Entity access methods (getById, findById) with proper error handling
  • Relation queries (findRelation, queryRelations, countRelations) for single and multiple entities
  • Edge cases and error validation

The test structure is well-organized with proper lifecycle management and clear test descriptions.

packages/query-mikro-orm/project.json (1)

1-43: LGTM!

The Nx project configuration is well-structured with proper build, test, lint, version, and publish targets. The implicit dependency on "core" correctly reflects the package's dependency on @ptc-org/nestjs-query-core.

packages/query-mikro-orm/__tests__/module.spec.ts (1)

1-69: LGTM!

Well-structured integration tests that verify the NestJS module functionality:

  • Query service creation for multiple entities
  • Custom DTO support with proper token resolution
  • MikroOrmModule export verification
  • Proper cleanup in afterEach to prevent resource leaks

The tests effectively validate the module's integration with NestJS and MikroORM.

packages/query-mikro-orm/jest.config.ts (1)

1-18: LGTM!

The Jest configuration follows standard practices and correctly sets up testing for the new MikroORM package.

packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts (1)

5-33: LGTM!

The entity relationships are correctly configured and bidirectional. The OneToMany, ManyToOne, ManyToMany, and OneToOne decorators properly reference TestRelation with correct inverse mappings and ownership settings.

packages/query-mikro-orm/__tests__/__fixtures__/connection.fixture.ts (1)

8-32: LGTM!

The test connection setup is appropriate for integration testing. The in-memory SQLite database with allowGlobalContext: true is acceptable for tests, and the truncate function correctly deletes TestRelation before TestEntity to respect foreign key constraints.

packages/query-mikro-orm/src/module.ts (1)

7-20: LGTM!

The module correctly wires MikroORM integration with NestJS Query. It properly extracts entity classes, creates query service providers, and delegates to MikroOrmModule.forFeature for entity registration.

packages/query-mikro-orm/__tests__/__fixtures__/seeds.ts (1)

32-72: LGTM!

The seed function correctly establishes test data with a two-phase approach: first persisting entities and relations, then setting up relation collections. The entityIndex = Math.floor(index / 3) calculation properly assigns 3 relations to each entity.

packages/query-mikro-orm/__tests__/__fixtures__/test-relation.entity.ts (1)

5-21: LGTM!

The TestRelation entity correctly defines bidirectional relationships with TestEntity. The ownership sides are properly configured: ManyToMany with owner: true on this side, and OneToOne without owner on this side (complementing TestEntity's ownership).

packages/query-mikro-orm/src/providers.ts (1)

14-51: LGTM!

The provider factory correctly handles three scenarios: custom assembler, DTO with registered assembler, and entity-only (no transformation). The token resolution properly uses getQueryServiceToken for the provide token and getRepositoryToken for dependency injection.

packages/query-mikro-orm/src/services/mikro-orm-query.service.ts (6)

21-27: LGTM!

Clean class structure with appropriate constructor accepting the repository and optional assembler for DTO/Entity transformations.


57-73: LGTM!

Query method correctly handles filter conversion, sorting, and paging with proper assembler integration.


75-99: LGTM!

Filter conversion correctly handles and/or combinators with proper validation preventing mixed filter structures.


161-202: LGTM!

Relation finding logic correctly handles both single and batch entity scenarios with clear error messaging for unsupported withDeleted option.


204-229: LGTM with minor observation.

The relation loading logic handles various MikroORM entity states (unloaded, Reference-wrapped, uninitialized). The fallback to em.refresh on line 226 assumes relationRef is a managed entity, which should hold true in this context.


402-418: LGTM!

Sort direction conversion correctly handles all combinations of ASC/DESC with NULLS_FIRST/NULLS_LAST positioning.


- name: Cache node modules
id: cache
uses: actions/cache@v3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

latest version actions/cache GitHub Action

💡 Result:

The current recommended release line is v4 — use actions/cache@v4 (or pin to the latest v4 tag/sha, e.g. v4.2.0). [1][2]

Sources:
[1] actions/cache GitHub repo (README).
[2] GitHub Changelog / deprecation notice.


Update actions/cache to v4.

The current recommended release line is v4. Consider updating from actions/cache@v3 to actions/cache@v4 (or pin to the latest v4.2.0).

🤖 Prompt for AI Agents
In .github/actions/setup-step/action.yml around line 15, the workflow uses
actions/cache@v3 which is outdated; update the action reference to the v4
release line (e.g., actions/cache@v4 or pin to actions/[email protected]) so the
workflow uses the current recommended version; replace the reference and verify
the inputs/behavior remain compatible with v4.

Comment on lines +29 to +40
async getById(id: string | number, opts?: GetByIdOptions<DTO>): Promise<DTO> {
const where = this.convertFilter(opts?.filter)
const entity = await this.repo.findOneOrFail({
...where,
id
} as unknown as FilterQuery<Entity>)

if (this.assembler) {
return this.assembler.convertToDTO(entity)
}
return entity as unknown as DTO
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded id property may fail for entities with different primary key names.

MikroORM entities can have primary keys with names other than id (e.g., uuid, _id, or custom names via @PrimaryKey()). This implementation will fail for such entities.

Consider using MikroORM's metadata API to dynamically resolve the primary key field:

 async getById(id: string | number, opts?: GetByIdOptions<DTO>): Promise<DTO> {
   const where = this.convertFilter(opts?.filter)
+  const meta = this.repo.getEntityManager().getMetadata().get(this.repo.getEntityName())
+  const pkField = meta.primaryKeys[0]
   const entity = await this.repo.findOneOrFail({
     ...where,
-    id
+    [pkField]: id
   } as unknown as FilterQuery<Entity>)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getById(id: string | number, opts?: GetByIdOptions<DTO>): Promise<DTO> {
const where = this.convertFilter(opts?.filter)
const entity = await this.repo.findOneOrFail({
...where,
id
} as unknown as FilterQuery<Entity>)
if (this.assembler) {
return this.assembler.convertToDTO(entity)
}
return entity as unknown as DTO
}
async getById(id: string | number, opts?: GetByIdOptions<DTO>): Promise<DTO> {
const where = this.convertFilter(opts?.filter)
const meta = this.repo.getEntityManager().getMetadata().get(this.repo.getEntityName())
const pkField = meta.primaryKeys[0]
const entity = await this.repo.findOneOrFail({
...where,
[pkField]: id
} as unknown as FilterQuery<Entity>)
if (this.assembler) {
return this.assembler.convertToDTO(entity)
}
return entity as unknown as DTO
}

Comment on lines +142 to +156
if (k === 'like') {
return ['$like', v as string] satisfies ['$like', OperatorMap<string>['$like']]
}

if (k === 'notLike') {
return ['$like', { $not: v as string }]
}

if (k === 'iLike') {
return ['$ilike', v as string] satisfies ['$ilike', OperatorMap<string>['$ilike']]
}

if (k === 'notILike') {
return ['$ilike', { $not: v as string }]
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

MikroORM FilterQuery $not operator $like $ilike syntax documentation

💡 Result:

Use a FilterQuery where $not wraps the comparison operator. Examples:

  • NOT LIKE:
    { name: { $not: { $like: '%foo%' } } }

  • NOT ILIKE (Postgres only):
    { name: { $not: { $ilike: '%foo%' } } }

You can also combine with logical operators, e.g.:
{ $and: [{ title: { $not: { $like: '%draft%' } } }, { active: true }] }

See MikroORM query operators and $not description. [1][2]

Sources:
[1] MikroORM — Query conditions (operators list, $like, $ilike, $not).
[2] MikroORM — Working with Entity Manager (FilterQuery usage examples).


🏁 Script executed:

cd packages/query-mikro-orm && grep -n "expandFilterComparison" src/services/mikro-orm-query.service.ts | head -20

Repository: TriPSs/nestjs-query

Length of output: 182


🏁 Script executed:

cd packages/query-mikro-orm && sed -n '1,160p' src/services/mikro-orm-query.service.ts | grep -A 50 "expandFilter\("

Repository: TriPSs/nestjs-query

Length of output: 182


Incorrect negation pattern for notLike and notILike operators.

MikroORM requires { $not: { $like: pattern } } structure, not { $not: value } nested inside the operator. The current implementation returns an invalid negation pattern that will not produce the expected "NOT LIKE" query.

   if (k === 'notLike') {
-    return ['$like', { $not: v as string }]
+    return ['$not', { $like: v as string }]
   }

   if (k === 'notILike') {
-    return ['$ilike', { $not: v as string }]
+    return ['$not', { $ilike: v as string }]
   }
🤖 Prompt for AI Agents
In packages/query-mikro-orm/src/services/mikro-orm-query.service.ts around lines
142 to 156, the `notLike` and `notILike` branches return an incorrect negation
shape (`{ $not: value }`) which MikroORM expects as `{ $not: { $like: pattern }
}` (and `{ $not: { $ilike: pattern } }` for case-insensitive). Update those
branches to wrap the operator inside the `$not` object so `notLike` returns the
same operator as `like` but with `{ $not: { $like: v as string } }` and
`notILike` returns `{ $not: { $ilike: v as string } }`, keeping the other
branches unchanged.

Comment on lines +231 to +249
private async matchesFilter<Relation extends object>(
relation: Relation,
filter: Filter<Relation>
): Promise<Relation | undefined> {
const em = this.repo.getEntityManager()
const where = this.convertFilter(filter as unknown as Filter<Entity>) as unknown as FilterQuery<Relation>
const wrapped = wrap(relation, true)
const pk = wrapped.getPrimaryKey()

const found = await em.findOne(
relation.constructor as Class<Relation>,
{
...where,
[wrapped.__meta.primaryKeys[0]]: pk
} as FilterQuery<Relation>
)

return (found as Relation) ?? undefined
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assumes single primary key; may fail with composite keys.

Line 244 uses primaryKeys[0] which won't work for entities with composite primary keys. Additionally, relation.constructor (line 241) may not return the expected class for MikroORM proxy objects.

For composite key support, consider building the primary key condition dynamically:

const pkCondition = wrapped.__meta.primaryKeys.reduce((acc, pkName) => {
  acc[pkName] = (relation as Record<string, unknown>)[pkName]
  return acc
}, {} as Record<string, unknown>)
🤖 Prompt for AI Agents
In packages/query-mikro-orm/src/services/mikro-orm-query.service.ts around lines
231 to 249, the code assumes a single primary key and uses relation.constructor
which can be wrong for MikrORM proxy objects; replace the single-key lookup with
a dynamically built primary-key condition by iterating
wrapped.__meta.primaryKeys and extracting each key's value from the relation
into an object (pkCondition), use the entity class from wrapped.__meta.class (or
equivalent meta target) instead of relation.constructor when calling em.findOne,
and pass the combined where + pkCondition as the FilterQuery so composite
primary keys and proxy classes are handled correctly.

Comment on lines +374 to +381
const [relationEntity] = relationEntities
const entityClass = (
relationEntity as unknown as {
__proto__: {
constructor: Class<RelationEntity>
}
}
).__proto__.constructor
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid deprecated __proto__ accessor.

Using __proto__ is deprecated and may not work reliably in all JavaScript environments. Use Object.getPrototypeOf() instead.

-  const entityClass = (
-    relationEntity as unknown as {
-      __proto__: {
-        constructor: Class<RelationEntity>
-      }
-    }
-  ).__proto__.constructor
+  const entityClass = Object.getPrototypeOf(relationEntity).constructor as Class<RelationEntity>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [relationEntity] = relationEntities
const entityClass = (
relationEntity as unknown as {
__proto__: {
constructor: Class<RelationEntity>
}
}
).__proto__.constructor
const [relationEntity] = relationEntities
const entityClass = Object.getPrototypeOf(relationEntity).constructor as Class<RelationEntity>
🤖 Prompt for AI Agents
In packages/query-mikro-orm/src/services/mikro-orm-query.service.ts around lines
374 to 381, the code uses the deprecated __proto__ accessor to obtain the
constructor of relationEntity; replace that with
Object.getPrototypeOf(relationEntity).constructor to avoid using __proto__.
Specifically, after extracting const [relationEntity] = relationEntities,
compute the entityClass by calling
Object.getPrototypeOf(relationEntity).constructor (and keep the existing typing
assertions as needed) so you use the standard Object.getPrototypeOf API instead
of __proto__.

@TriPSs
Copy link
Owner

TriPSs commented Dec 10, 2025

@jtomaszewski can you checkout the comments of coderabbit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants