feat(examples): add React Router framework example#368
feat(examples): add React Router framework example#368jkomyno wants to merge 3 commits intotml-2294-vite-support-matrixfrom
Conversation
Closes April milestone VP3 by demonstrating invisible contract emission inside a Vite-based framework. Part of TML-2298 / the vite-vp3-auto-emit project. The example is a minimal React Router v7.14 Framework Mode app with one index route exposing both a loader (SELECT users) and an action (INSERT user + redirect). The Prisma Next runtime lives in `app/lib/db.server.ts` behind a `getDb()` function that stashes the client on globalThis, mirroring the prior art in `examples/retail-store/src/db-singleton.ts`. Pool size is capped at 1 so the smoke test stays compatible with @prisma/dev's single-connection PGlite harness. A single `prisma-next.config.ts` supports both PSL and TS authoring, gated on `PRISMA_NEXT_CONTRACT_SOURCE`. No sibling `.ts-contract.ts` file, no `--config` flag — one env flip at dev-server startup. The smoke test (`test/react-router.smoke.e2e.test.ts`) stands up a PGlite database via `@prisma-next/test-utils`, boots Vite programmatically via `createServer`, POSTs to the action, GETs the loader, edits `prisma/schema.prisma` mid-flight, and asserts the contract re-emits with the new column. TS re-emit is already covered at the plugin level (`test/integration/test/vite-plugin.hmr.e2e.test.ts`) and is documented as a manual README repro to avoid doubling flake surface. Vite 7/8 compat is covered by APR-VP3-05's plugin matrix, not by this example's CI job, for the same reason. Updates the Vite plugin README to reference the new example alongside `prisma-next-demo`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/middleware-telemetry
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@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/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/runtime-executor
@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: |
| }); | ||
| } | ||
| return cached; | ||
| } |
There was a problem hiding this comment.
You can't use the postgres facade for this?
| const plan = db.sql.user.insert({ email }).returning('id', 'email').build(); | ||
| await db.runtime().execute(plan); |
| // Prisma Next's marker table plus our own model tables. Kept inline so the test | ||
| // reads top-to-bottom without chasing a fixture file. | ||
| const TEST_SCHEMA_SQL = ` | ||
| create schema if not exists prisma_contract; | ||
| create table if not exists prisma_contract.marker ( | ||
| id smallint primary key default 1, | ||
| core_hash text not null default '', | ||
| profile_hash text not null default '', | ||
| contract_json jsonb, | ||
| canonical_version int, | ||
| updated_at timestamptz not null default now(), | ||
| app_tag text, | ||
| meta jsonb not null default '{}' | ||
| ); | ||
| create table if not exists "user" ( | ||
| id uuid primary key default gen_random_uuid(), | ||
| email text not null, | ||
| "createdAt" timestamptz not null default now() | ||
| ); | ||
| create table if not exists "post" ( | ||
| id uuid primary key default gen_random_uuid(), | ||
| title text not null, | ||
| "userId" uuid not null references "user"(id), | ||
| "createdAt" timestamptz not null default now() | ||
| ); | ||
| `; |
There was a problem hiding this comment.
Why are you using raw sql instead of the programmatic Prisma Next api (control client)?
- db.server.ts: register an import.meta.hot.dispose handler so HMR after a contract re-emit rebuilds the runtime from the fresh contractJson instead of reusing the stale cached client. APR-VP3-07 will replace this with hash-keyed caching; this is minimum hygiene until then. - smoke test teardown: revert prisma/schema.prisma *before* closing the Vite server and wait for the plugin to re-emit the clean contract. Previously the schema was reverted but the server was torn down before the watcher drained, leaving src/prisma/contract.json reflecting the mid-test edit and dirtying the working tree. - smoke test: type JSON.parse result as `unknown` and narrow via toMatchObject (matches CLAUDE.md "never use any"); assert that the PSL replace() actually mutated the file so a future schema reformat doesn't silently no-op the test; drop the unused `_` param on the loader. - vitest.config.ts: align with examples/prisma-next-demo — environment, pool, maxWorkers, isolate; use the @prisma-next/test-utils timeouts catalog helper instead of hard-coded 60_000ms.
…nd prisma/ The root turbo.json declares `test`/`typecheck`/`lint` inputs as `src/**` + `test/**`, which matches the shape used by every other workspace in the repo. React Router Framework Mode keeps route modules under `app/**` and contract authoring under `prisma/**`, so without a local override turbo would cache this example's test result across changes to the loader, action, or db.server.ts. CI fresh checkouts are unaffected, but local `pnpm test` at repo root could replay stale success.
Summary
examples/react-router-demo— a React Router v7.14 Framework Mode example with one index route exposing both a loader (SELECT) and an action (INSERT + redirect) that run through@prisma-next/postgreson the server viaapp/lib/db.server.ts(getDb()pattern, mirroringexamples/retail-store/src/db-singleton.ts)@prisma-next/test-utils.createDevDatabase), POSTs the action, GETs the loader, editsprisma/schema.prismamid-flight, and asserts the contract re-emits with the new column without a manual commandprisma-next.config.tsgated onPRISMA_NEXT_CONTRACT_SOURCE=ts|psl— no sibling.ts-contract.tsfile, no--configflag@prisma-next/vite-plugin-contract-emit's README to point at bothprisma-next-demoandreact-router-demoPlan-vs-reality deltas
@prisma-next/postgresexposespoolOptionsbut only for timeouts. Cap via an externally-constructednew Pool({ connectionString, max: 1 })passed aspg:, which is public API.max: 1is required so the app cohabits with@prisma/dev's single-connection PGlite.db.sql.user.insert({ email }).returning(...)(the same pattern asexamples/prisma-next-demo/src/queries/dml-operations.ts) rather than the ORMcreate()path, which requires explicit id/createdAt with branded-type casts./?indexper React Router's convention for disambiguating index routes from their parent layout.test/integration/test/vite-plugin.hmr.e2e.test.ts. Running it a second time through React Router would double flake surface without adding signal.Testing
All four green locally.
Tracking
projects/vite-vp3-auto-emit/— ticketapr-vp3-06-react-router-example.md, milestone 3getDb()cache with a hash-keyed dev helper so HMR doesn't leave a stale runtime after re-emit