RB-001Mục đích: Sự cố

Vercel + TanStack Start + Nitro (pnpm Nx monorepo)

#vercel#tanstack-start#nitro#pnpm#nx#monorepo#github-packages#ssr#build-output-api
Kiểm mục
  • Vercel Root Directory = repo root (or adjust Nitro output.dir relative to app package)
  • vercel.json: framework: "tanstack-start", omit outputDirectory
  • buildCommand runs nx build {project} --skip-nx-cache (or separate cache key for Vercel builds)
  • installCommand sets GitHub Packages auth without mutating committed .npmrc
  • Vercel env: GITHUB_TOKEN (PAT, read:packages) on Production + Preview
  • Vercel env: DATABASE_URL (Postgres) when app reads CMS content at runtime
  • .npmrc: @polyms:registry + registry.npmjs.org — no token in git
  • .vercelignore: root-anchored patterns only (/docs/, /agents/) — bare segment/ matches nested app paths too
  • apps/{project}/vite.config.ts: Nitro vercel preset + monorepo output.dir when VERCEL=1
  • Commit generated routeTree.gen.ts (or ensure route files are not stripped by ignore rules)
  • Verify: rm -rf .vercel/output && vercel build → success; config.json has /__server

Stack profile

Văn bản

Outputs (two modes)

CommandVERCELOutput pathPurpose
pnpm build (local)unsetapps/{project}/.output/Local preview (pnpm preview)
vercel build / Vercel CI1repo root .vercel/output/Build Output API v3 for deploy

Nitro vercel preset must be active when VERCEL=1. In a monorepo with Vercel root = repo root, Nitro output.dir must point to ../../.vercel/output from apps/{project}/.

Build Output API layout (expected)

.vercel/output/
├── config.json          # routes include "dest": "/__server"
├── static/
│   └── assets/          # NOT static/static/
└── functions/
    └── __server.func/   # SSR handler — NOT under static/

config.json (minimal SSR check)

Must contain a catch-all route to the server function:

{ "src": "/(.*)", "dest": "/__server" }

Repo files (monorepo app)

FileRole
vercel.jsonframework: tanstack-start, custom install/build commands, no outputDirectory
scripts/vercel-install.shGitHub Packages auth before pnpm install
.vercelignoreShrink upload; root-anchored patterns only
.npmrcScope registry only — no committed auth token
apps/{project}/vite.config.tsnitro preset vercel + monorepo output.dir when VERCEL=1

GitHub Packages auth

Sự cố

pnpm install fails on Vercel for @polyms/core-ui; 401/404 from npm.pkg.github.com.

Nguyên nhân
  • Vercel does not expand GITHUB_TOKEN in a committed project .npmrc (pnpm 11+ ignores it for security).
  • Appending tokens to .npmrc during install pollutes git and duplicates lines on retry.
Cách sửa
  • Vercel project env: GITHUB_TOKEN = GitHub PAT with read:packages (Production + Preview).
  • installCommand: bash scripts/vercel-install.sh — exports token, sets user .npmrc auth, then pnpm install --frozen-lockfile.
  • Committed .npmrc: registry + scope only.
Xác minh
  • bash scripts/vercel-install.sh
  • # install completes; no token lines added to ./.npmrc

Nx cache vs Vercel output

Sự cố

vercel build succeeds once, then fails with No Output Directory named "dist" (or empty/wrong .vercel/output/config.json).

Nguyên nhân
  • Nx cache hit from a prior local pnpm build where VERCEL was unset. That run restores apps/{project}/.output/ but not repo root .vercel/output/. Vercel CLI then falls back to looking for dist.
Cách sửa
  • vercel.json buildCommand must include --skip-nx-cache on nx build {project}, or
  • Use a dedicated Nx target for Vercel whose inputs include VERCEL=1, or
  • Run pnpm exec nx reset before debugging locally.
Xác minh
  • rm -rf .vercel/output
  • vercel build
  • test -f .vercel/output/config.json
  • grep -q '__server' .vercel/output/config.json

Wrong outputDirectory

Sự cố

No Output Directory named ".output" after build, or .vercel/output/static/static/assets/, or config.json routes everything to /404.html without dest: /__server.

Nguyên nhân
  • Setting outputDirectory in vercel.json makes Vercel CLI treat Nitro Build Output API tree as a static folder and wrap/copy it incorrectly.
Cách sửa
  • Remove outputDirectory from vercel.json.
  • Let Nitro emit Build Output API directly to repo root .vercel/output/ (configure output.dir in apps/{project}/vite.config.ts for monorepo).
  • Keep framework: "tanstack-start".
Xác minh
  • rm -rf .vercel/output && vercel build
  • find .vercel/output/static -maxdepth 2 -type d # expect static/assets, NOT static/static
  • grep '"dest": "/__server"' .vercel/output/config.json

.vercelignore excludes app routes

Sự cố

TypeScript: "/{route}/$id" not in route union. Or Vercel build passes but dynamic pages 404 / router tree missing routes.

Nguyên nhân
  • Unanchored pattern (e.g. {route}/) in .vercelignore matches any directory with that name, including apps/{project}/src/routes/{route}/. TanStack Router regenerates a reduced route tree without those files.
Cách sửa
  • Anchor ignore patterns to repo root only: /docs/, /agents/, /demo/
  • Not bare {route}/ (matches everywhere under the upload tree).
Xác minh
  • apps/{project}/src/routes/{route}/ present in deployment upload.
  • apps/{project}/src/routeTree.gen.ts includes /{route}/$id.
  • pnpm exec tsc --noEmit in apps/{project}/ passes.

DATABASE_URL missing at runtime

Sự cố

CMS-backed routes return 500 or empty; PrismaClientInitializationError in server logs.

Nguyên nhân
  • DATABASE_URL not set on Vercel or local .env for the Start app.
  • Migrations or seed not applied to the Postgres instance.
Cách sửa
  • Set DATABASE_URL to Postgres URI on Vercel (Production + Preview) and in .env.local for local dev.
  • From apps/{project}/: pnpm db:migrate && pnpm db:seed after DATABASE_URL is configured.
  • Use Supabase pooled URI for serverless; direct URL for migrations if provider requires it.
Xác minh
  • curl -s http://localhost:{port}/… | head # CMS-backed page HTML, not 500
  • pnpm db:seed in apps/{project}/ completes without error

Static CI expects dist, Start uses Nitro build output

Sự cố

GitHub Actions (or similar) uploads dist/ artifact but nx/vite build writes Nitro output — workflow green but deployed site 404.

Nguyên nhân
  • Deploy workflow written for static Vite SPA (dist/) before TanStack Start + Nitro adoption.
  • Start/Nitro local preview and Vercel Build Output API use Nitro build dir or .vercel/output/, not dist/.
Cách sửa
  • Prefer Vercel with vercel.json for TanStack Start production deploy.
  • Or update CI artifact path to match Start output (.vercel/output after vercel build, or Nitro preview dir).
  • Remove stale dist/ upload steps referencing apps/{project}/dist/.
Xác minh
  • ls apps/{project}/.output/ after pnpm build # exists
  • test ! -d apps/{project}/dist || echo "dist/ is legacy — do not deploy"

TanStack Router vs Start version mismatch

Sự cố

Build fails: cannot resolve ./ssr/server from @tanstack/react-router, or peer dependency warnings on react-router vs react-start.

Nguyên nhân
  • @tanstack/react-router version below @tanstack/react-start requirement (≥ 1.168 for SSR export).
  • Legacy @tanstack/router-plugin still installed alongside Start — conflicting route generation.
Cách sửa
  • Align @tanstack/react-router with @tanstack/react-start peer range in apps/{project}/package.json.
  • Remove @tanstack/router-plugin when Start is the bundler entry.
  • @tanstack/react-start replaces router-plugin — entry is src/router.tsx + __root.tsx document shell.
Xác minh
  • pnpm build in apps/{project}/ succeeds
  • No duplicate router-plugin in package.json