Vercel + TanStack Start + Nitro (pnpm Nx monorepo)
- 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ảnOutputs (two modes)
| Command | VERCEL | Output path | Purpose |
|---|---|---|---|
| pnpm build (local) | unset | apps/{project}/.output/ | Local preview (pnpm preview) |
| vercel build / Vercel CI | 1 | repo 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)
| File | Role |
|---|---|
| vercel.json | framework: tanstack-start, custom install/build commands, no outputDirectory |
| scripts/vercel-install.sh | GitHub Packages auth before pnpm install |
| .vercelignore | Shrink upload; root-anchored patterns only |
| .npmrc | Scope registry only — no committed auth token |
| apps/{project}/vite.config.ts | nitro 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
Thứ tự đọc · 8