Mongo ORM: extendable Collection class + collections registry (ADR 175)#936
Mongo ORM: extendable Collection class + collections registry (ADR 175)#936ankur-arch wants to merge 5 commits into
Conversation
The Mongo ORM's chaining implementation was locked behind the
MongoCollection interface and createMongoCollection factory, so users
could not do what the SQL ORM supports:
class UserRepository extends Collection<Contract, 'User'> {
byEmail(email: string) { return this.where({ email }); }
}
The implementation class is now the exported, extendable `Collection`
(mirroring @prisma-next/sql-orm-client). Chaining methods clone through
this.constructor so a chain started from a subclass stays that subclass.
mongoOrm() and the mongo() facade accept a `collections` registry keyed
by model name; registered roots surface the subclass (typed via
InstanceType), unregistered roots keep the base collection.
The shared-interface extraction across families stays future work per
ADR 175's spike-then-extract plan; the ADR carries a status note.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds a subclassable Mongo ORM ChangesCustom Collection subclassing support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant mongo
participant mongoOrm
participant Collection
participant MongoQueryExecutor
mongo->>mongoOrm: mongoOrm(contract, executor, collections)
mongoOrm->>Collection: instantiate registered subclass
Collection->>MongoQueryExecutor: run chained query
mongoOrm-->>mongo: MongoClient with custom roots
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Comment |
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/extension-supabase
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/config-loader
@prisma-next/emitter
@prisma-next/language-server
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/3-extensions/mongo/src/runtime/mongo.ts (1)
34-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated
Collectionsconstraint into a shared alias.The clause
Collections extends Partial<Record<string, AnyMongoCollectionClass>> = Record<never, never>is duplicated 7 times in this file. A shared type alias would reduce drift risk if the constraint ever changes.♻️ Proposed refactor
+export type MongoCollectionsMap = Partial<Record<string, AnyMongoCollectionClass>>; + export interface MongoClient< TContract extends MongoContractWithTypeMaps<MongoContract, AnyMongoTypeMaps>, - Collections extends Partial<Record<string, AnyMongoCollectionClass>> = Record<never, never>, + Collections extends MongoCollectionsMap = Record<never, never>, > {(and apply the same substitution at the other six occurrences)
Also applies to: 67-67, 83-83, 99-99, 127-127, 131-131, 137-137
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/3-extensions/mongo/src/runtime/mongo.ts` at line 34, The `Collections` generic constraint is repeated across several declarations in `mongo.ts`, so extract `Collections extends Partial<Record<string, AnyMongoCollectionClass>> = Record<never, never>` into a shared type alias and replace all seven occurrences with that alias. Update the affected generic signatures in the relevant Mongo types/functions so the constraint is defined once and reused consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/architecture` docs/adrs/ADR 175 - Shared ORM Collection interface.md:
- Line 5: The Mongo ORM example text in the ADR is stale and conflicts with the
updated status note; refresh the example heading and surrounding wording in the
ADR section that references the Mongo ORM target so it reflects that custom
collections are already implemented. Use the nearby Mongo example and the
shared-interface “spike then extract” language as anchors, and make sure the
wording is internally consistent with the `mongoOrm({ collections })`, `mongo({
collections })`, and extendable `Collection` behavior now described.
In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts`:
- Line 301: The include refinement in Collection should not use a bare
production cast; replace the existing `as unknown as Collection<...>` in the
include-cloning path with `blindCast<...>('include() refines the TIncludes
phantom type after cloning with the added include expression')` so the intent is
explicit and the no-bare-casts rule stays enforceable.
- Around line 830-839: The fluent query methods on Collection are widening
custom subclasses back to MongoCollection, which drops subclass-specific methods
after chaining. Update the return typing for the fluent builders in Collection
so methods like where, select, take, skip, orderBy, and include preserve the
current subtype via this or a self-type, and use `#clone` as the place to ensure
subclass instances are returned. Add a d.ts test covering a custom repository
subclass (for example UserRepository with a byName method) chained through
multiple query steps to verify the subclass type is retained.
In `@packages/2-mongo-family/5-query-builders/orm/src/mongo-orm.ts`:
- Line 77: The final return in mongo-orm.ts still uses a bare type assertion;
replace the `client as MongoOrmClient<TContract, Collections>` cast with the
existing `blindCast` helper to keep production code consistent. Update the
return in the `createMongoOrm` flow so the `MongoOrmClient` value is wrapped via
`blindCast` instead of using `as`, and keep the already imported `blindCast`
symbol as the location anchor.
---
Nitpick comments:
In `@packages/3-extensions/mongo/src/runtime/mongo.ts`:
- Line 34: The `Collections` generic constraint is repeated across several
declarations in `mongo.ts`, so extract `Collections extends
Partial<Record<string, AnyMongoCollectionClass>> = Record<never, never>` into a
shared type alias and replace all seven occurrences with that alias. Update the
affected generic signatures in the relevant Mongo types/functions so the
constraint is defined once and reused consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 6ec48a0c-9547-414e-99dc-c670fd24bffd
📒 Files selected for processing (10)
docs/architecture docs/adrs/ADR 175 - Shared ORM Collection interface.mdpackages/2-mongo-family/5-query-builders/orm/src/collection.tspackages/2-mongo-family/5-query-builders/orm/src/exports/index.tspackages/2-mongo-family/5-query-builders/orm/src/mongo-orm.tspackages/2-mongo-family/5-query-builders/orm/test/custom-collection.test-d.tspackages/2-mongo-family/5-query-builders/orm/test/custom-collection.test.tspackages/3-extensions/mongo/src/exports/runtime.tspackages/3-extensions/mongo/src/runtime/mongo.tspackages/3-extensions/mongo/test/mongo.e2e.test.tspackages/3-extensions/mongo/test/mongo.test.ts
- where/select/orderBy/take/skip return `this` on both the MongoCollection interface and the Collection class, so custom -> base -> custom chains (repo.byName(x).take(1).newestFirst()) typecheck; include()/variant() re-parameterize generics and deliberately widen to the base type. - AnyMongoCollectionClass now requires the Collection type-level brand (string-key brand, mirroring ENUM_TYPE_HANDLE_BRAND), so only real Collection subclasses can be registered. - Custom construction is centralized in instantiateCustomCollection with an instanceof guard that throws a clear error for non-Collection classes. - Remaining production `as unknown as` casts replaced with blindCast + reason. - ADR 175 example no longer claims the Mongo side is unimplemented and shows the shipped object-form where/orderBy API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
…ngo collections surface Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
|
Ponytail review (lazy-senior-dev ladder over the final diff):
→ skipped: callback where-DSL on Mongo, subclass typing through |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
What this ships
You can now write custom repository classes for the Mongo ORM, exactly like the SQL ORM (ADR 175):
Before this PR,
MongoCollectionwas interface-only —extends Collectionwas impossible on Mongo. This was reported on Discord ([NEXT], 2026-07-08); follow-up to #934.The changes
Collectionis exported and extendable from@prisma-next/mongo-orm(and@prisma-next/mongo/runtime). It is the same chaining implementation that already backed the interface — no behavior change.mongoOrm({ collections })andmongo({ collections })register subclasses by model name. Registered roots are typed as your subclass; everything else keeps the base type. Existing call sites compile unchanged.where,select,orderBy,take, andskipreturnthis, sousers.byEmail(x).take(1).newestFirst()typechecks.include()andvariant()change the collection's generic parameters, so they intentionally widen to the base type (documented on the class).Collectionsubclasses — enforced at the type level (a brand on the class) and at runtime (a clear error if a non-Collectionclass is registered from untyped code).Review feedback addressed (CodeRabbit + ponytail pass)
as unknown ascasts in the touched files replaced withblindCast+ reason.AnyMongoCollectionClasstightened from=> objectto the branded instance shape.instanceofguard and clear error message.#cloneWithVariantnow delegates to#clone. Skipped: the SQL-style callback where-DSL on Mongo and subclass typing throughinclude()/variant()— add when the cross-family where-DSL generalizes (ADR 175's extraction step).Testing
mongo-orm(registry wiring, plan parity with the base collection, subclass preservation, brand rejection, runtime guard).mongo({ collections })forwarding (unit) and a full round-trip against a real in-memory replica set (e2e).pnpm test:packagessweep (926/928 files; the 2 failures are machine-local flakes that reproduce on cleanmainand pass in isolation), mongo + emit integration suites,mongo-demoexample suite,pnpm lint:deps, typecheck/lint on both packages.🤖 Generated with Claude Code
Summary by CodeRabbit
collectionsregistry to the Mongo ORM (mongoOrm) andmongofacade, enabling customCollectionsubclasses per model.variant()and other query-chain methods).Collectionnow throw a clear runtime error.