docs(skills): prisma-next-queries — Postgres surface is always namespace-qualified#917
docs(skills): prisma-next-queries — Postgres surface is always namespace-qualified#917ankur-arch wants to merge 6 commits into
Conversation
…s namespace-qualified Corrections verified against @prisma-next 0.14.0 and current main while writing the Prisma Next Fundamentals docs (prisma/web#8011): - The flat `db.orm.User` / `db.sql.user` form does not exist on Postgres. #778 removed the flat fallback; the orm() proxy resolves only namespace keys, so bare model access returns `undefined` at runtime. All Postgres examples now use `db.orm.public.<Model>` / `db.sql.public.<table>`, and the "flat form still works for single-namespace contracts" claim is replaced with the tested behavior. - `.count()` is not a collection terminal (it throws "count() is only available inside include() refinement callbacks"). Counting goes through `.aggregate((a) => ({ n: a.count() }))`; the terminal list is corrected. - SQL-builder inserts must pass rows as an array. The single-object `.insert({...})` form fails at result decoding on 0.14.0; the example now uses `.insert([{...}])` and notes the constraint. 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 (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughDocumentation updates across ChangesNamespace-qualified accessor documentation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@skills/prisma-next-queries/postgres.md`:
- Around line 111-117: The pagination example builds the next cursor from
page1[page1.length - 1] in the Post query flow, which will fail when the first
page is empty. Update the Post pagination snippet to guard the empty result from
db.orm.public.Post.orderBy(...).take(20).all() before deriving last or
constructing page2, and only continue to the next page when page1 has at least
one item.
In `@skills/prisma-next-queries/SKILL.md`:
- Around line 124-126: The Postgres short-script example is not self-contained
because the `users` iterable is missing, so the snippet cannot be copied and run
as shown. Update the example around the `for (const u of users)` and
`db.orm.public.User.create` usage by adding a concrete `users` binding or an
inline seed source in the same snippet so the loop has defined input.
- Around line 79-84: The buffered query example uses the projected shape from
User.select('id', 'email'), so the type annotation should match the returned row
shape instead of the full entity type. Update the Promise<User[]> annotation in
the buffered example to Promise<Row[]> and keep the surrounding prose/examples
consistent with the Row[] terminology used by
db.orm.public.User.select(...).all().toArray().
🪄 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: 18448aa6-cceb-44ae-a59f-adc5980ca23e
📒 Files selected for processing (2)
skills/prisma-next-queries/SKILL.mdskills/prisma-next-queries/postgres.md
| ``` | ||
|
|
||
| The flat `db.sql.users` / `db.orm.User` form still works when bare names are unique across all namespaces. When the same bare name appears in more than one namespace, use the coordinate form — both the type system and the runtime require it to resolve to the right table. | ||
| There is no flat `db.sql.users` / `db.orm.User` form: the namespace coordinate is always required on Postgres. The runtime proxy resolves only namespace keys, so a bare model access returns `undefined` (verified against 0.14.0 and current `main`; the flat fallback was removed in #778). |
There was a problem hiding this comment.
Do we need this historical fun fact in the skill?
There was a problem hiding this comment.
Agreed, dropped. It now states the rule plainly: the namespace coordinate is always required on Postgres, and a bare model access returns undefined so the first chained call throws. Same cleanup applied to the equivalent sentence in SKILL.md. Fixed in c9cf152.
|
|
||
| - **`db.orm.<Model>`** — ORM, PascalCase model name (`db.orm.User`). Fluent `.where(...).select(...).orderBy(...).all()`, fully typed against `Contract`. Default lane for CRUD with relations. | ||
| - **`db.sql.<table>`** — SQL builder, lowercase storage name (`db.sql.user`). Produces a *plan* executed via `db.runtime().execute(plan)`. Use when the ORM is too high-level — explicit `JOIN`, computed projections, set operations, window functions. | ||
| - **`db.orm.<ns>.<Model>`** — ORM, namespace-qualified PascalCase model name (`db.orm.public.User`). Fluent `.where(...).select(...).orderBy(...).all()`, fully typed against `Contract`. Default lane for CRUD with relations. |
There was a problem hiding this comment.
I think we need to mention a bit on how ns relates to PSL and contract. We always use either public or placeholder <ns> throughout the doc. We should mention somewhere that public is default (for the models declarated outside of namespace block in PSL) and <ns> stand for a namespace model have been explicitly defined in.
There was a problem hiding this comment.
Good call. Added a paragraph to the Key Concepts section (and mirrored it in SKILL.md's namespace section): models declared at the top level of the PSL live in the default public namespace, and models declared inside a namespace <name> { ... } block are addressed by that namespace; the examples use public and readers substitute their own. Fixed in c9cf152.
SevInf
left a comment
There was a problem hiding this comment.
Preemtively approve, but there are few things I'd like to change before we merge it
- Explain where the namespace coordinate comes from: top-level PSL
models live in the default public namespace, models inside a
namespace <name> { } block are addressed by that namespace (SevInf).
- Drop the historical note about the flat fallback's removal; state
the rule plainly (SevInf).
- Guard the empty first page before deriving the cursor in the
pagination example (CodeRabbit).
- Promise<User[]> -> Promise<Row[]> for consistency with the
surrounding Row[] text (CodeRabbit).
- Make the short-script seed example self-contained by defining the
users array it iterates (CodeRabbit).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
…alified-orm-surface Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com> # Conflicts: # skills/prisma-next-queries/SKILL.md
…alified-orm-surface
…nsert note Address review of the qualified-ORM-surface pass. Three flat accessors survived the original sweep and contradicted the PR own thesis: the Supabase routing row (Supabase is precisely the multi-namespace case), pitfall 6 teaching the crashing db.sql.<tableName> shape, and two lane-table placeholders. The insert note misdescribed the failure. A bare object is a compile-time type error (insert() is typed ReadonlyArray), and when cast past, the throw happens in plan build, not result decoding. The array form is the designed signature, not a workaround, so it is now presented as such and the version-pinned "verified on 0.14.0" framing is gone. Also restores SQLite to the frontmatter description and adds a note in postgres.md, since SQLite readers are routed to this guide but use the flat form: its facade pre-binds the single unbound namespace. Signed-off-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
Refresh pass — conflict resolved, claims re-verified against
|
| Claim | Verdict | Evidence |
|---|---|---|
| Postgres access is always namespace-qualified | ✅ still true | The ORM root proxy resolves only namespace names (Object.hasOwn(contract.domain.namespaces, prop)) with no flat fallback — packages/3-extensions/sql-orm-client/src/orm.ts:158-171. A bare model access is undefined. |
.count() is not a query terminal |
✅ still true | Collection-level count() unconditionally calls #assertIncludeRefinementMode('count()') (sql-orm-client/src/collection.ts:725-728), which throws at :2364. .aggregate((a) => ({ n: a.count() })) is the correct replacement. |
Single-object .insert({...}) doesn't work |
Still array-only on main (sql-builder/src/types/table-proxy.ts:88-92 types it ReadonlyArray; the .map happens in mutation-impl.ts:252-256). See below. |
Corrections applied on top of the original PR
1. Three flat accessors survived the original sweep and contradicted the PR's own thesis:
SKILL.md:50— the Supabase routing row still advertiseddb.orm.<Model>+db.sql.<table>. Supabase is precisely the multi-namespace Postgres case (public/auth), so this was the worst place to leave the flat form.postgres.mdpitfall 6 — a pitfall entry teaching the correct shape was itself teaching the crashingdb.sql.<tableName>.select(...).- Two lane-decision-table placeholders (
db.sql.<t>).
2. The insert note was wrong about the mechanism, not just stale. A bare object is a compile-time type error — the crash only reaches runtime for untyped or cast callers — and the throw happens during plan build, not "result decoding". More importantly, the array form is the designed signature, not a workaround. The note now reads as ".insert(...) takes an array of rows, even for a single row" and the version-pinned (verified on 0.14.0) framing is gone, so it can't rot again.
3. SQLite was silently dropped from the skill. The rewritten frontmatter described only Postgres, yet SQLite users are still routed to postgres.md — whose examples are now all public-qualified. I verified SQLite genuinely does keep flat access (packages/3-extensions/sqlite/src/runtime/sqlite.ts:52 pre-indexes UNBOUND_NAMESPACE_ID before handing over db.orm), so SQLite is back in the description and postgres.md carries a note telling SQLite readers to drop the public. segment.
I also double-checked the PR's "SQLite and Mongo keep flat access" sentence rather than assuming it — SQLite goes through the same sql-orm-client proxy as Postgres, which made it look like an overshoot. It isn't: the facade unwraps the unbound namespace first. The sentence is correct as written.
Checks run
pnpm lint:skills ✅ (all skills pass frontmatter validation) · pnpm lint:docs ✅ · pnpm check:upgrade-coverage --mode pr ✅
Docs-only change — no build/test surface. An exhaustive grep of both files confirms every remaining flat db.orm.X / db.sql.x string is intentional (they describe the form that doesn't work, or are Mongo's db.orm.<root>).
Remaining risks / for human review
- The
create-prismascaffold templates are still broken and are NOT fixed here. The PR body flags thatseed.ts/index.ts/users.tsemit the crashing flat form, so the scaffold'sdb:seedscript fails on a fresh Postgres project. That package lives in a different repo, so it can't be fixed in this PR — but it's the highest-impact instance of the same defect and needs its own issue. This is the main thing worth a decision. mongo.mdwas not touched and was out of scope for this review; Mongo's flat surface is correct, but it hasn't been re-verified against 0.15.0.- The insert limitation is a product rough edge being documented. If single-object insert is meant to work, the real fix is in
sql-builder, and this note should later be deleted rather than maintained.
size-limit report 📦
|
@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: |
bd27f14 to
3efa36a
Compare
What this fixes
The
prisma-next-queriesskill teaches agents (and people) how to query Prisma Next. Three things it teaches no longer match how the released packages behave. Anyone following the skill on a Postgres project today writes code that crashes on its first query.This PR updates the skill to the tested behavior. Every correction was verified by running real queries against
@prisma-next0.14.0 on a live Postgres database, and cross-checked against the source onmain.The major change: Postgres access is always namespace-qualified
Since #778, the ORM and SQL builder resolve models through the schema namespace only:
The flat form does not error helpfully.
db.orm.Userisundefined, so the first chained call throwsCannot read properties of undefined (reading 'where'). The skill said the flat form "still works for single-namespace contracts", which is exactly the setup every new project has, so the guidance broke the most common case.Impact: every Postgres example in the skill produced crashing code. This also affects the
create-prismascaffold templates (seed.ts,index.ts,users.tsstill use the flat form, sonpm run db:seedcrashes on a fresh scaffold). That template fix is separate from this PR and still needed.Two smaller corrections
.count()is not a query terminal. Calling it throwscount() is only available inside include() refinement callbacks. Counting goes through.aggregate((a) => ({ n: a.count() })), which the skill's aggregate section already documents. The terminal list now reads.all() / .first() / .aggregate(...).SQL builder inserts need the array form.
.insert({ email })fails inside result decoding on 0.14.0 (this[#rows].map is not a function)..insert([{ email }])works, including.returning(...)and generated-id defaults. The example now uses the array form and notes the constraint.How this was verified
The same validation run surfaced a few adjacent issues that are out of scope here but worth tracking: the scaffold templates above, MongoDB
@default(now())not being applied at create time, and pipeline.matchon ObjectId fields matching nothing. Happy to file them separately.🤖 Generated with Claude Code
Summary by CodeRabbit
undefined.