diff --git a/README.md b/README.md
index b2f6716..0b6a57e 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,8 @@ Convex writes repo-root deployment configuration to `.env.local` during local se
The local Convex bootstrap now mirrors `CONVEX_URL` into `apps/web/.env.local` as `NEXT_PUBLIC_CONVEX_URL` so the web app can read the placeholder `health:status` query through the Convex client runtime. If you want the homepage to show live backend data instead of the local configuration fallback, run `pnpm bootstrap:backend:local` first and keep `pnpm dev:backend:local` running while you use `pnpm dev:web`.
+The first server-side `Next.js -> Convex` baseline now lives at `/server-status`. It uses `fetchQuery` from `convex/nextjs` on a dedicated route rendered dynamically, while the homepage keeps the reactive client-side `useQuery` path.
+
`pnpm verify` is the full repo verification pass and now includes the local Convex bootstrap checks. If you are iterating on the web app only, use `pnpm verify:web` for the lighter web-only path.
## Start here
diff --git a/apps/web/README.md b/apps/web/README.md
index 3e1b552..3a9d475 100644
--- a/apps/web/README.md
+++ b/apps/web/README.md
@@ -28,4 +28,5 @@ pnpm build:web
- language baseline: `TypeScript`
- styling baseline: `Tailwind CSS`
- the app mounts a Convex provider baseline and the homepage performs a live `health:status` query when `apps/web/.env.local` contains `NEXT_PUBLIC_CONVEX_URL`; the local Convex bootstrap now mirrors that value automatically from the repo-root Convex bootstrap output
+- the server-side Convex baseline lives at `/server-status` and uses `fetchQuery` from a server component without replacing the reactive client pattern on `/`
- auth, billing, and deployment wiring still belong to follow-on issues
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index cde069e..30d92a1 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -1,3 +1,4 @@
+import Link from "next/link";
import { BackendStatusCard } from "./backend-status-card";
export default function Home() {
@@ -34,6 +35,12 @@ export default function Home() {
>
Next.js docs
+
+ Server-side baseline
+
- A clean frontend baseline
+ Client and server baselines
- The repo now has a real web surface under{" "}
- apps/web, ready for
- local development, Convex integration, linting, and production
- builds.
+ The repo now has one reactive client read and one explicit server-side
+ read pattern under apps/web,
+ ready to support later profile and auth work without mixing patterns.
- Add the first server-side data baseline
+ Define the profile data foundation
- The next meaningful milestone is documenting the first server-side
- Convex read path so App Router usage stays clean as real features
- land.
+ The next meaningful milestone is establishing the first durable
+ profile schema so public pages and claim flows can build on typed
+ domain records instead of the bootstrap health placeholder.
diff --git a/apps/web/src/app/server-status/page.tsx b/apps/web/src/app/server-status/page.tsx
new file mode 100644
index 0000000..7145c38
--- /dev/null
+++ b/apps/web/src/app/server-status/page.tsx
@@ -0,0 +1,148 @@
+import Link from "next/link";
+import { fetchBackendStatus } from "@/convex/server";
+
+export const dynamic = "force-dynamic";
+
+export default async function ServerStatusPage() {
+ const status = await fetchBackendStatus();
+
+ return (
+
+
+
+
+
+ VRDex
+ Server-side Convex baseline
+
+
+
+
+ First server-side App Router read path.
+
+
+ This route demonstrates the baseline Next.js{" "}
+ server-component pattern for Convex in VRDex: use fetchQuery{" "}
+ for a server-only read, and keep useQuery for
+ reactive client surfaces.
+
+ Run pnpm bootstrap:backend:local so the local
+ bootstrap writes NEXT_PUBLIC_CONVEX_URL into
+ apps/web/.env.local before using this
+ server-side route.
+
+ >
+ ) : (
+ <>
+
+ Convex server read failed
+
+
+ Start pnpm dev:backend:local, confirm
+ NEXT_PUBLIC_CONVEX_URL is available to the web app,
+ and reload this page.
+
+ >
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/convex/server.ts b/apps/web/src/convex/server.ts
new file mode 100644
index 0000000..7dc740e
--- /dev/null
+++ b/apps/web/src/convex/server.ts
@@ -0,0 +1,25 @@
+import { fetchQuery } from "convex/nextjs";
+import { api } from "@convex-generated-api";
+
+export async function fetchBackendStatus() {
+ if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
+ return { kind: "missing-url" as const };
+ }
+
+ try {
+ const data = await fetchQuery(api.health.status, {});
+
+ return {
+ kind: "live" as const,
+ data,
+ };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : String(error);
+
+ console.error(`Server-side Convex fetchQuery failed: ${message}`);
+
+ return {
+ kind: "error" as const,
+ };
+ }
+}
diff --git a/docs/backend/convex-bootstrap.md b/docs/backend/convex-bootstrap.md
index b79f096..4e67fb8 100644
--- a/docs/backend/convex-bootstrap.md
+++ b/docs/backend/convex-bootstrap.md
@@ -19,6 +19,7 @@ It is intentionally narrow: enough structure to run Convex locally, generate typ
- `convex.json` pins Convex to Node `22` so local backend runtime expectations stay aligned with the repo's current Node baseline
- `convex/tsconfig.json` provides the TypeScript settings Convex uses to typecheck backend source files
- the web app consumes `health:status` as the first real `Next.js -> Convex` runtime path, mounting a client-side provider baseline and surfacing the result in the homepage without inventing early product schema
+- the web app also exposes `/server-status`, where a server component uses `fetchQuery` against the same `health:status` function as the initial server-side baseline
## Local workflow
@@ -36,6 +37,7 @@ Notes:
- Convex writes deployment configuration to the repo-root `.env.local` file.
- The local Convex wrapper mirrors the repo-root `CONVEX_URL` into `apps/web/.env.local` as `NEXT_PUBLIC_CONVEX_URL` so the web app can follow the normal client-side Convex + Next.js convention without leaking a non-public variable through server props.
+- That same `NEXT_PUBLIC_CONVEX_URL` value is also what `fetchQuery` uses for the current server-side baseline route, so local client and server reads share one deployment setting.
- Anonymous local backend state for this repo is kept under `.convex-home/` and `.convex-tmp/` so the bootstrap does not collide with other Convex projects on the same machine.
- The current bootstrap is local-development focused. Production deploy keys, preview deployments, and frontend environment wiring belong to follow-on issues.
- Committed files in `convex/_generated/` are treated as checked-in build artifacts and should remain diff-free after `pnpm check:backend:generated`.
@@ -51,5 +53,15 @@ Keep the initial backend slice simple:
## Follow-on issues
- `#55` wires the web app to the first Convex client/runtime path using `health:status`
-- `#64` should add the first server-side `Next.js -> Convex` data path once the client runtime baseline is stable
+- `#64` adds the first server-side `Next.js -> Convex` data path with `fetchQuery` on `/server-status`
- schema, auth, billing, and production deployment posture should land in their own issues instead of bloating the bootstrap
+
+## App Router baseline
+
+The current rule is intentionally narrow:
+
+- use `useQuery` inside client components when the UI should stay reactive after first render
+- use `fetchQuery` inside server components when the page only needs a server-side read
+- defer `preloadQuery` until a real feature needs server-rendered first paint plus a hydrated reactive handoff
+
+This keeps the first App Router pattern copyable without introducing multiple competing baselines before auth and domain work arrive.
diff --git a/docs/planning/engineering-strategy.md b/docs/planning/engineering-strategy.md
index 6637471..6f2d16e 100644
--- a/docs/planning/engineering-strategy.md
+++ b/docs/planning/engineering-strategy.md
@@ -51,6 +51,7 @@ Current recommendation:
- keep the first backend slice schema-light with an explicit health query instead of guessing at product tables too early
- use local-development-friendly Convex setup first, then layer in frontend wiring, auth, billing, and production deployment posture through follow-on issues
- once the local backend bootstrap is deterministic, include it in the baseline PR verification pass alongside the web checks
+- use one explicit server-side App Router baseline once client wiring is stable: `fetchQuery` for server-only reads, with `preloadQuery` deferred until a feature truly needs hydrated reactivity after server render
## Monetization direction