diff --git a/API_ROUTES_TIMEOUT_INVESTIGATION.md b/API_ROUTES_TIMEOUT_INVESTIGATION.md new file mode 100644 index 0000000..575a6bd --- /dev/null +++ b/API_ROUTES_TIMEOUT_INVESTIGATION.md @@ -0,0 +1,932 @@ +# API Routes Timeout Issue - Complete Investigation Report + +## Problem Statement + +API routes deployed to Vercel are timing out with no response. The Next.js application pages work perfectly, but all API endpoints at `/api/*` return timeouts or "Redirecting..." messages. + +**Affected URLs:** +- `https://tpmjs.com/api/health` - Returns "Redirecting..." +- `https://tpmjs.com/api/tools` - Returns "Redirecting..." +- `https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/health` - Timeouts (exit code 28) +- `https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/tools` - Timeouts (exit code 28) + +**Working:** +- All page routes work correctly (e.g., `/`, `/tool/[slug]`) +- UI navigation and client-side routing function normally +- Local development API routes work perfectly + +## Environment Details + +### Project Structure +- **Monorepo:** Turborepo setup with pnpm workspaces +- **Framework:** Next.js 16.0.4 (App Router) +- **Node Version:** 24.x (on Vercel) +- **Deployment Platform:** Vercel +- **Custom Domains:** tpmjs.com, www.tpmjs.com + +### Repository Structure +``` +tpmjs/ +├── apps/ +│ └── web/ # Next.js 16 App Router application +│ ├── src/ +│ │ └── app/ +│ │ ├── api/ +│ │ │ ├── health/route.ts +│ │ │ ├── stats/route.ts +│ │ │ ├── tools/ +│ │ │ │ ├── route.ts +│ │ │ │ ├── [id]/route.ts +│ │ │ │ ├── [slug]/route.ts +│ │ │ │ └── validate/route.ts +│ │ │ └── sync/ +│ │ │ ├── changes/route.ts +│ │ │ ├── keyword/route.ts +│ │ │ └── metrics/route.ts +│ │ ├── page.tsx +│ │ └── tool/[slug]/page.tsx +│ ├── next.config.ts +│ └── vercel.json +├── packages/ +│ ├── db/ # Prisma client +│ ├── types/ # Shared TypeScript types +│ ├── utils/ # Utility functions +│ ├── env/ # Environment validation +│ └── ui/ # React component library +├── vercel.json # Root Vercel configuration +└── turbo.json +``` + +## API Route Examples + +### `/apps/web/src/app/api/health/route.ts` +```typescript +import { NextResponse } from 'next/server'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 60; + +/** + * GET /api/health + * Simple health check endpoint that doesn't touch the database + */ +export async function GET() { + return NextResponse.json({ + status: 'ok', + timestamp: new Date().toISOString(), + env: { + hasDatabase: !!process.env.DATABASE_URL, + nodeEnv: process.env.NODE_ENV, + }, + }); +} +``` + +### `/apps/web/src/app/api/tools/route.ts` +```typescript +import { NextResponse } from 'next/server'; +import { prisma } from '@tpmjs/db/client'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; + +export async function GET(request: Request) { + // ... query string parsing + + const [tools, totalCount] = await Promise.all([ + prisma.tool.findMany({ + where, + orderBy: [ + { qualityScore: 'desc' }, + { npmDownloadsLastMonth: 'desc' }, + { createdAt: 'desc' }, + ], + take: limit, + skip: offset, + }), + prisma.tool.count({ where }), + ]); + + return NextResponse.json({ + data: tools, + pagination: { + page, + limit, + total: totalCount, + totalPages: Math.ceil(totalCount / limit), + }, + }); +} +``` + +## Configuration Files + +### `/apps/web/next.config.ts` (Current) +```typescript +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = { + transpilePackages: ['@tpmjs/ui', '@tpmjs/utils', '@tpmjs/db', '@tpmjs/types', '@tpmjs/env'], + reactStrictMode: true, +}; + +export default nextConfig; +``` + +### `/apps/web/vercel.json` (Current) +```json +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build", + "installCommand": "pnpm install" +} +``` + +### `/vercel.json` (Root) +```json +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "git": { + "deploymentEnabled": { + "main": true + } + }, + "github": { + "silent": false, + "autoJobCancelation": true + }, + "crons": [ + { + "path": "/api/sync/changes", + "schedule": "*/2 * * * *" + }, + { + "path": "/api/sync/keyword", + "schedule": "*/15 * * * *" + }, + { + "path": "/api/sync/metrics", + "schedule": "0 * * * *" + } + ] +} +``` + +## Local Build Verification + +### Local Build Output Structure +```bash +$ ls -R /Users/ajaxdavis/repos/tpmjs/tpmjs/apps/web/.next/server/app/api/ + +health/ +stats/ +sync/ +tools/ + +/apps/web/.next/server/app/api/health: +route +route.js +route.js.map +route.js.nft.json +route_client-reference-manifest.js + +/apps/web/.next/server/app/api/stats: +route +route.js +route.js.map +route.js.nft.json +route_client-reference-manifest.js + +/apps/web/.next/server/app/api/tools: +[id]/ +[slug]/ +validate/ +route +route.js +route.js.map +route.js.nft.json +route_client-reference-manifest.js +``` + +### Routes Manifest Confirmation +```bash +$ cat /apps/web/.next/routes-manifest.json | jq '.staticRoutes[] | select(.page | contains("api"))' + +{ + "page": "/api/health", + "regex": "^/api/health(?:/)?$", + "routeKeys": {}, + "namedRegex": "^/api/health(?:/)?$" +} +{ + "page": "/api/stats", + "regex": "^/api/stats(?:/)?$", + "routeKeys": {}, + "namedRegex": "^/api/stats(?:/)?$" +} +{ + "page": "/api/sync/changes", + "regex": "^/api/sync/changes(?:/)?$", + "routeKeys": {}, + "namedRegex": "^/api/sync/changes(?:/)?$" +} +{ + "page": "/api/sync/keyword", + "regex": "^/api/sync/keyword(?:/)?$", + "routeKeys": {}, + "namedRegex": "^/api/sync/keyword(?:/)?$" +} +{ + "page": "/api/sync/metrics", + "regex": "^/api/sync/metrics(?:/)?$", + "routeKeys": {}, + "namedRegex": "^/api/sync/metrics(?:/)?$" +} +``` + +### Node File Trace (NFT) Verification +```bash +$ cat /apps/web/.next/server/app/api/health/route.js.nft.json + +{ + "version": 1, + "files": [ + "../../../../../../../node_modules/.pnpm/next@16.0.4_@babel+core@7.28.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/dist/client/components/app-router-headers.js", + "../../../../../../../node_modules/.pnpm/next@16.0.4_@babel+core@7.28.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/dist/compiled/@opentelemetry/api/index.js", + // ... many more dependencies + ] +} +``` + +**Conclusion:** API routes build correctly locally with all dependencies properly traced. + +## Vercel Deployment Analysis + +### Deployment Inspection Output +```bash +$ vercel inspect https://tpmjs-1chh44d1u-tpmjs.vercel.app + +General + id dpl_2FHyiTWtBZzvT8EcohwYEZmdb8rb + name tpmjs-web + target production + status ● Ready + url https://tpmjs-1chh44d1u-tpmjs.vercel.app + created Fri Nov 28 2025 20:44:03 GMT+1000 + +Aliases + ╶ https://www.tpmjs.com + ╶ https://tpmjs-web.vercel.app + ╶ https://tpmjs-web-tpmjs.vercel.app + ╶ https://tpmjs-web-git-main-tpmjs.vercel.app + ╶ https://tpmjs.com + +Builds + ┌ . [0ms] + ├── λ tool/[slug] (562.92KB) [iad1] + ├── λ tool/[slug].rsc (562.92KB) [iad1] + ├── λ _global-error (642.48KB) [iad1] + ├── λ _global-error.rsc (642.48KB) [iad1] + ├── λ _global-error.segments/__PAGE__.segment.rsc (642.48KB) [iad1] + └── 56 output items hidden +``` + +**CRITICAL FINDING:** No API routes are listed in the build output. Only pages (`tool/[slug]`, `_global-error`, etc.) appear as serverless functions (`λ`). + +Expected API routes that should appear: +- `λ api/health` +- `λ api/tools` +- `λ api/tools/[id]` +- `λ api/tools/[slug]` +- `λ api/sync/changes` +- etc. + +### Testing Results +```bash +# Direct Vercel URL - Timeouts +$ curl -s -m 10 https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/health +# Exit code 28 (timeout) + +# Custom Domain - Returns "Redirecting..." +$ curl -s -m 10 https://tpmjs.com/api/health +Redirecting... + +# Custom Domain - Returns "Redirecting..." +$ curl -s -m 10 https://tpmjs.com/api/tools +Redirecting... + +# Check redirect headers +$ curl -I https://tpmjs.com +HTTP/2 307 +cache-control: public, max-age=0, must-revalidate +content-type: text/plain +date: Fri, 28 Nov 2025 10:38:12 GMT +location: https://www.tpmjs.com/ +server: Vercel +``` + +## Vercel Configuration Details + +### User-Confirmed Settings +- **Root Directory:** `apps/web` (set in Vercel dashboard) +- **DATABASE_URL:** Configured in Vercel environment variables (Production) +- **Framework Preset:** (Unknown - needs verification) +- **Build Output Directory:** (Unknown - using default `.next`) + +### Project List +```bash +$ vercel project ls | grep -i tpmjs + +tpmjs -- 16h 24.x +v0-tool-registry-page https://tpmjs.com 3d 22.x +``` + +**NOTE:** Two projects exist: +1. `tpmjs` - Current project (Node 24.x) +2. `v0-tool-registry-page` - Also has tpmjs.com domain (Node 22.x) + +This could indicate a domain routing conflict or outdated project. + +## Investigation Timeline & Attempts + +### Attempt 1: Remove Root-Level Redirects +**Hypothesis:** The redirect in `/vercel.json` was intercepting API requests. + +**Original `/vercel.json`:** +```json +{ + "redirects": [ + { + "source": "/:path*", + "has": [ + { + "type": "host", + "value": "www.tpmjs.com" + } + ], + "destination": "https://tpmjs.com/:path*", + "permanent": true + } + ] +} +``` + +**Action:** Removed the `redirects` array from root `/vercel.json`. + +**Result:** ❌ API routes still timeout. Redirect rule was not the root cause. + +**Commit:** `9be3af7 fix(routing): move www redirect from vercel.json to Next.js config` + +### Attempt 2: Move Redirects to Next.js Config +**Hypothesis:** Next.js should handle redirects after routing. + +**Action:** Added `async redirects()` to `apps/web/next.config.ts`: +```typescript +async redirects() { + return [ + { + source: '/:path*', + has: [ + { + type: 'host', + value: 'www.tpmjs.com', + }, + ], + destination: 'https://tpmjs.com/:path*', + permanent: true, + }, + ]; +} +``` + +**Result:** ❌ API routes returned "Redirecting..." instead of executing. Next.js `async redirects()` applies to ALL routes including API routes. + +**Commit:** `9be3af7 fix(routing): move www redirect from vercel.json to Next.js config` + +### Attempt 3: Exclude API Routes from Redirect +**Hypothesis:** Use regex to exclude `/api/*` from redirects. + +**Action:** Modified redirect pattern: +```typescript +async redirects() { + return [ + { + source: '/((?!api).*)', // Negative lookahead to exclude /api/* + has: [ + { + type: 'host', + value: 'www.tpmjs.com', + }, + ], + destination: 'https://tpmjs.com/$1', + permanent: true, + }, + ]; +} +``` + +**Result:** ❌ API routes back to timing out (not redirecting anymore, but still not working). + +**Commit:** `c42bfb6 fix(redirects): exclude API routes from www redirect` + +### Attempt 4: Remove All Redirects +**Hypothesis:** Eliminate redirect loop causing ERR_TOO_MANY_REDIRECTS. + +**Action:** Removed `async redirects()` entirely from `next.config.ts`. + +**Result:** ✅ Redirect loop fixed. ❌ API routes still timeout. + +**Commit:** `b93cd42 fix: remove redirects to resolve redirect loop` + +### Attempt 5: Add Vercel Functions Configuration +**Hypothesis:** Vercel needs explicit configuration to detect API routes. + +**Action:** Added to `apps/web/vercel.json`: +```json +{ + "functions": { + "app/api/**/*.ts": { + "maxDuration": 60 + } + } +} +``` + +**Result:** ❌ No change. API routes still timeout. + +**Commit:** `748ca4d fix(api): configure Vercel functions for API routes with maxDuration` + +### Attempt 6: Add maxDuration to Route Files +**Hypothesis:** Export configuration directly in route handlers. + +**Action:** Added to `apps/web/src/app/api/health/route.ts`: +```typescript +export const maxDuration = 60; +``` + +**Result:** ❌ No change. API routes still timeout. + +**Commit:** `8281f8f fix(build): disable Turbopack for Vercel deployment` + +### Attempt 7: Disable Turbopack +**Hypothesis:** Turbopack (Next.js 16 default) has compatibility issues with Vercel. + +**Action:** Added `--webpack` flag to build command: +```json +{ + "buildCommand": "cd ../.. && turbo build --filter=@tpmjs/web -- --webpack" +} +``` + +**Result:** ❌ Build failed completely. Invalid flag syntax. + +**Commit:** `8281f8f fix(build): disable Turbopack for Vercel deployment` + +### Attempt 8: Simplify Build Command +**Hypothesis:** Use direct pnpm build instead of Turbo wrapper. + +**Action:** Changed to: +```json +{ + "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build" +} +``` + +**Result:** ⏳ Pending deployment test. + +**Commit:** `065196d fix(build): simplify Vercel build command` + +## Root Cause Analysis + +### What We Know FOR SURE + +1. ✅ **API routes build correctly locally** + - All 9 API routes compile to `.next/server/app/api/` + - NFT (Node File Trace) files are generated with proper dependencies + - Routes manifest includes all API routes + +2. ✅ **Next.js configuration is correct** + - `export const runtime = 'nodejs'` set correctly + - `export const dynamic = 'force-dynamic'` set correctly + - `transpilePackages` includes all workspace packages + +3. ✅ **Pages deploy and work perfectly** + - `/tool/[slug]` renders correctly + - Homepage loads + - Client-side navigation works + +4. ❌ **API routes are NOT deployed as serverless functions** + - `vercel inspect` shows NO API routes in build output + - Only pages appear as `λ` (lambda) functions + - This is the PRIMARY issue + +5. ❌ **Direct Vercel URLs timeout** + - Not just a custom domain issue + - Affects `*.vercel.app` URLs + - Exit code 28 (timeout) - no response at all + +6. ❌ **Custom domain shows "Redirecting..."** + - Even with all redirects removed from config + - Suggests a redirect at Vercel platform level OR DNS level + - Could be from the `v0-tool-registry-page` project conflict + +### Possible Root Causes + +#### Theory 1: Vercel Project Misconfiguration +**Likelihood:** HIGH + +**Evidence:** +- Two projects with same domain (`tpmjs` and `v0-tool-registry-page`) +- Framework Preset might not be set to "Next.js" +- Root Directory is `apps/web` but Vercel might not be detecting Next.js properly + +**What to Check:** +1. Vercel Dashboard → Project Settings → General + - Framework Preset: Should be "Next.js" + - Root Directory: Should be "apps/web" + - Build Command: Should match vercel.json + - Output Directory: Should be blank (default `.next`) + +2. Vercel Dashboard → Domains + - Check if both projects have tpmjs.com + - Remove domain from `v0-tool-registry-page` if present + +3. Vercel Dashboard → Deployments → Build Logs + - Search for "API" or "route" + - Look for errors about missing functions + - Check if Next.js is detected correctly + +#### Theory 2: Monorepo Detection Issue +**Likelihood:** MEDIUM + +**Evidence:** +- Build command uses `cd ../.. && pnpm --filter=@tpmjs/web build` +- Vercel might not be correctly detecting workspace structure +- `transpilePackages` includes workspace packages + +**What to Check:** +1. Build logs for workspace resolution errors +2. Check if `node_modules` is being created in correct location +3. Verify pnpm workspace configuration + +**Potential Fix:** +Try setting `installCommand` to: +```json +{ + "installCommand": "pnpm install --shamefully-hoist" +} +``` + +#### Theory 3: Next.js 16 + Vercel Incompatibility +**Likelihood:** MEDIUM + +**Evidence:** +- Next.js 16 released recently (November 2024) +- Turbopack is default (might have Vercel issues) +- App Router API routes behave differently than Pages Router + +**What to Check:** +1. Vercel build logs for Next.js version detection +2. Any warnings about incompatible features +3. Check Vercel's Next.js 16 support status + +**Potential Fix:** +Downgrade to Next.js 15.x temporarily to test: +```json +{ + "dependencies": { + "next": "^15.0.0" + } +} +``` + +#### Theory 4: Environment Variable Issue +**Likelihood:** LOW + +**Evidence:** +- DATABASE_URL is configured +- Pages work (they might not need env vars) +- API routes use Prisma (requires DATABASE_URL) + +**What to Check:** +1. Vercel Dashboard → Settings → Environment Variables + - Verify DATABASE_URL is set for Production + - Verify it's not blocked or empty + - Check if other vars are needed + +2. Build logs for Prisma generation errors + +**Potential Fix:** +None - user confirmed DATABASE_URL is set. + +#### Theory 5: Build Output Issue +**Likelihood:** MEDIUM-HIGH + +**Evidence:** +- `vercel inspect` doesn't show API routes +- Only pages are listed as functions +- Build completes successfully (38-41 seconds) + +**What to Check:** +1. Build logs: Does Next.js report building API routes? + - Look for "Route (app)" or "λ" indicators for API routes + - Compare to local build output + +2. Check if Vercel is using correct build output structure + - App Router uses `.next/server/app/` + - Pages Router uses `.next/server/pages/` + +**Potential Fix:** +Try forcing Vercel to recognize the build: +```json +{ + "builds": [ + { + "src": "package.json", + "use": "@vercel/next" + } + ] +} +``` + +(Note: `builds` is legacy, modern Next.js should auto-detect) + +## Recommended Next Steps + +### Immediate Actions (High Priority) + +1. **Check Vercel Project Settings** + - Go to Vercel Dashboard → tpmjs-web project + - Verify Framework Preset is "Next.js" + - Verify Root Directory is `apps/web` + - Screenshot settings for reference + +2. **Review Build Logs** + - Go to latest deployment + - Download complete build logs + - Search for: + - "Route (app)" - should show API routes + - "λ" - should show API functions + - "api" - any mentions + - Errors or warnings + +3. **Check Domain Configuration** + - Verify only ONE project has tpmjs.com domain + - Remove domain from `v0-tool-registry-page` project if present + - Check DNS settings aren't redirecting + +4. **Test Simple API Route** + - Create minimal API route: + ```typescript + // apps/web/src/app/api/test/route.ts + export async function GET() { + return new Response('Hello from API', { status: 200 }); + } + ``` + - Deploy and test + - If this doesn't work, confirms platform issue + +### Investigation Actions (Medium Priority) + +5. **Compare Working vs Non-Working** + - Find a deployment where pages DO work + - Compare build output between page routes and API routes + - Look for differences in how they're compiled + +6. **Test Vercel CLI Deploy** + - Deploy directly via CLI: `vercel --prod` + - Check if behavior differs from Git-based deploy + - Might reveal configuration issues + +7. **Check Vercel Function Logs** + - Even though functions aren't in build output, try: + - `vercel logs --since 1h` + - Look for any API route invocations or errors + +8. **Review Turbo Configuration** + ```bash + # Check turbo.json for Next.js build config + cat /turbo.json + + # Verify build runs correctly locally + pnpm --filter=@tpmjs/web build + ``` + +### Alternative Approaches (If Above Fails) + +9. **Create New Vercel Project** + - Import from Git fresh + - Use identical settings + - Test if fresh project works + +10. **Contact Vercel Support** + - This may be a platform bug with Next.js 16 + - Provide this document as context + - Ask specifically why API routes aren't in build output + +11. **Temporary Workaround** + - Deploy API routes separately (different service) + - Use Vercel proxy to route `/api/*` to separate deployment + - Not ideal but unblocks development + +## Environment Variables Needed + +```bash +# Required for API routes +DATABASE_URL="postgresql://..." + +# Optional (check if needed) +NODE_ENV="production" +NEXT_PUBLIC_* # Any public env vars +``` + +## Build Commands Reference + +### Local Development +```bash +# Install dependencies +pnpm install + +# Generate Prisma client +pnpm --filter=@tpmjs/db db:generate + +# Run development server +pnpm --filter=@tpmjs/web dev + +# Build for production +pnpm --filter=@tpmjs/web build + +# Test build locally +pnpm --filter=@tpmjs/web start +``` + +### Vercel Configuration +**Current:** +```json +{ + "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build", + "installCommand": "pnpm install" +} +``` + +**Alternative to try:** +```json +{ + "buildCommand": "cd ../.. && turbo build --filter=@tpmjs/web", + "installCommand": "pnpm install", + "framework": "nextjs" +} +``` + +## Key Files to Review + +1. `/apps/web/next.config.ts` - Next.js configuration +2. `/apps/web/vercel.json` - Vercel app-level config +3. `/vercel.json` - Vercel root config +4. `/turbo.json` - Turborepo configuration +5. `/apps/web/.next/routes-manifest.json` - Route definitions +6. `/apps/web/.next/build-manifest.json` - Build output +7. Vercel build logs (from dashboard) + +## Questions for Vercel Support + +If escalating to Vercel support, ask: + +1. Why are API routes not appearing in the build output (`vercel inspect`) when pages are deploying correctly? + +2. Is there a known issue with Next.js 16 App Router API routes in Turborepo monorepos? + +3. What's the correct way to configure `vercel.json` for a Next.js 16 app in a monorepo with custom build commands? + +4. Could having two projects (`tpmjs` and `v0-tool-registry-page`) with the same domain cause routing issues? + +5. Are there any specific requirements for deploying Next.js 16 API routes that differ from Next.js 15? + +## Related Documentation + +- [Next.js 16 Upgrade Guide](https://nextjs.org/docs/app/guides/upgrading/version-16) +- [Vercel Next.js Deployment](https://vercel.com/docs/frameworks/nextjs) +- [Vercel Functions Configuration](https://vercel.com/docs/functions/configuring-functions) +- [Turborepo with Vercel](https://vercel.com/docs/monorepos/turborepo) +- [Next.js App Router API Routes](https://nextjs.org/docs/app/api-reference/file-conventions/route) + +## Recent Commits Related to This Issue + +``` +065196d fix(build): simplify Vercel build command +8281f8f fix(build): disable Turbopack for Vercel deployment +748ca4d fix(api): configure Vercel functions for API routes with maxDuration +b93cd42 fix: remove redirects to resolve redirect loop +c42bfb6 fix(redirects): exclude API routes from www redirect +9be3af7 fix(routing): move www redirect from vercel.json to Next.js config +a92dfff fix(build): add workspace packages to Next.js transpilePackages +cc6c824 fix(vercel): configure Turborepo monorepo build for apps/web +``` + +## ✅ CONCLUSION - ROOT CAUSE IDENTIFIED + +### The Real Problem + +**Vercel is NOT detecting this project as a Next.js application.** + +When Vercel doesn't detect Next.js, it: +- Uses `@vercel/static-builder` instead of `@vercel/next` +- Treats the deployment as a static site +- Deploys pages (static HTML) successfully +- **Completely drops all App Router API routes** +- Never generates serverless functions for `/api/*` routes + +This explains EVERY symptom: +- ✅ Pages work (they're static files) +- ❌ API routes timeout (they were never deployed) +- ❌ No `λ api/*` in build output (functions don't exist) +- ❌ Direct Vercel URLs timeout (not a DNS issue) +- ❌ "Redirecting..." on custom domain (wrong project owns the domain) + +### Why Vercel Doesn't Detect Next.js + +**1. Wrong Root Directory** +- Vercel project likely has Root Directory set to `.` or empty +- Should be exactly: `apps/web` +- A single character difference breaks Next.js detection + +**2. Wrong Framework Preset** +- When Vercel can't auto-detect Next.js (due to monorepo + wrong root) +- It defaults to Framework Preset = "Other" +- "Other" uses static builder, not Next.js builder + +**3. Domain Conflict** +- Two projects exist: `tpmjs` and `v0-tool-registry-page` +- Both have `tpmjs.com` domain assigned +- Requests randomly route to wrong project +- "Redirecting..." comes from the old project, not your code + +**4. Custom Build Commands** +- Custom build commands can bypass Vercel's auto-detection +- Should let Vercel auto-detect and use default commands + +### Evidence + +**Local build works:** +```bash +$ ls apps/web/.next/server/app/api/ +health/ stats/ sync/ tools/ +# All API routes compile correctly +``` + +**Vercel deployment missing API routes:** +```bash +$ vercel inspect https://tpmjs-1chh44d1u-tpmjs.vercel.app +Builds + ├── λ tool/[slug] # Pages present + ├── λ _global-error # Pages present + └── 56 output items hidden +# NO api/health, NO api/tools, NO api/* at all +``` + +This is **not a Next.js bug, not a Turborepo bug, not a code issue**. +This is **pure Vercel project misconfiguration**. + +### The Fix (4 Steps, 5 Minutes) + +See `VERCEL_FIX_CHECKLIST.md` for detailed instructions. + +**Quick version:** +1. Set Root Directory to `apps/web` (exactly) +2. Set Framework Preset to "Next.js" +3. Remove `tpmjs.com` domain from `v0-tool-registry-page` project +4. Clear custom build commands (let Vercel auto-detect) + +**No code changes needed. Just fix Vercel dashboard settings.** + +### After the Fix + +Next deployment will show: +```bash +$ vercel inspect +Builds + ├── λ api/health + ├── λ api/tools + ├── λ api/sync/changes + ├── λ api/tools/[id] + ├── λ api/tools/[slug] + ├── λ tool/[slug] + ... +``` + +And API routes will work instantly: +```bash +$ curl https://tpmjs.com/api/health +{"status":"ok","timestamp":"..."} +``` + +--- + +## Summary for ChatGPT + +The core issue is that **Vercel is not detecting the project as Next.js** due to incorrect Root Directory, Framework Preset set to "Other", domain conflicts between two projects, and custom build commands bypassing auto-detection. + +When Vercel doesn't detect Next.js, it uses a static site builder which deploys pages but **completely drops all App Router API routes** from the build. This is why pages work but all `/api/*` requests timeout - the API route serverless functions were never created. + +**The fix is purely configuration** - no code changes needed. Set Root Directory to `apps/web`, Framework Preset to "Next.js", remove the domain from the old project, and clear custom build commands. See `VERCEL_FIX_CHECKLIST.md` for step-by-step instructions. diff --git a/VERCEL_FIX_CHECKLIST.md b/VERCEL_FIX_CHECKLIST.md new file mode 100644 index 0000000..c3c184b --- /dev/null +++ b/VERCEL_FIX_CHECKLIST.md @@ -0,0 +1,324 @@ +# 🔥 Vercel API Routes Fix - Action Checklist + +## Problem Summary + +**API routes are not deploying because Vercel is not detecting your project as Next.js.** + +When Vercel doesn't detect Next.js, it treats your deployment as a static site and **drops all App Router API routes** from the build output. Pages work because they're static files, but API routes require serverless function generation which only happens when Next.js is properly detected. + +--- + +## ✅ Fix Checklist (Complete in Order) + +### 1️⃣ Fix Root Directory + +**Where:** Vercel Dashboard → Project `tpmjs-web` → Settings → General → Root Directory + +**Current (likely):** Empty, `.`, or wrong path +**Required:** `apps/web` (exactly this, no leading/trailing slashes) + +**Validation:** +``` +✓ Must be exactly: apps/web +✗ NOT: /apps/web/ +✗ NOT: ./apps/web +✗ NOT: tpmjs/apps/web +``` + +--- + +### 2️⃣ Set Framework Preset + +**Where:** Vercel Dashboard → Project `tpmjs-web` → Settings → General → Framework Preset + +**Current (likely):** "Other" +**Required:** "Next.js" + +**Why this matters:** +- Framework = "Other" → Uses `@vercel/static-builder` → No API routes +- Framework = "Next.js" → Uses `@vercel/next` → API routes deployed + +--- + +### 3️⃣ Remove Domain from Old Project + +**Where:** Vercel Dashboard → Project `v0-tool-registry-page` → Settings → Domains + +**Action:** Remove these domains: +- `tpmjs.com` +- `www.tpmjs.com` + +**Then:** Verify both domains are ONLY assigned to the `tpmjs` project + +**Why this matters:** +The "Redirecting..." message is coming from the old project. Having two projects with the same domain causes shadow routing and API requests hitting the wrong deployment. + +--- + +### 4️⃣ Clear Custom Build Commands + +**Where:** Vercel Dashboard → Project `tpmjs-web` → Settings → Build & Development Settings + +**Set ALL to default/empty:** +``` +Build Command: (empty - let Vercel auto-detect) +Install Command: (empty - let Vercel auto-detect) +Output Directory: .next (default) +``` + +**Why this matters:** +Custom build commands bypass Vercel's Next.js detection. Vercel should automatically: +- Detect monorepo structure +- Run `pnpm install` +- Run `pnpm build` in the correct workspace +- Use `@vercel/next` builder + +**If you must use custom commands, use:** +``` +Build Command: pnpm turbo run build --filter=@tpmjs/web +Install Command: pnpm install +``` + +But try empty first. + +--- + +## 🧪 Verification Steps + +### Before Deploying + +Run this locally to confirm Next.js detection: +```bash +cd apps/web +vercel build +``` + +**Expected output should include:** +``` +● route (app) /api/health +● route (app) /api/tools +● route (app) /api/sync/changes +λ /api/health +λ /api/tools +λ /api/sync/changes +``` + +**If you DON'T see this, Vercel won't deploy API routes.** + +### After Deploying + +1. **Check Build Output:** +```bash +vercel inspect +``` + +Should show: +``` +Builds + ├── λ api/health (XXX KB) [region] + ├── λ api/tools (XXX KB) [region] + ├── λ api/sync/changes (XXX KB) [region] + ├── λ tool/[slug] (XXX KB) [region] + ... +``` + +2. **Test API Routes:** +```bash +# Should return JSON (not timeout, not "Redirecting...") +curl https://tpmjs.com/api/health + +# Should return tool data +curl https://tpmjs.com/api/tools +``` + +--- + +## 📋 Expected Results + +### ✅ Success Indicators + +- [ ] `vercel inspect` shows API routes as `λ` functions +- [ ] `curl https://tpmjs.com/api/health` returns JSON +- [ ] `curl https://tpmjs.com/api/tools` returns tool data +- [ ] No "Redirecting..." messages +- [ ] No timeouts on direct Vercel URLs +- [ ] Build logs show "route (app) /api/*" + +### ❌ Failure Indicators (Need to revisit steps) + +- [ ] Only pages listed in `vercel inspect`, no API routes +- [ ] API endpoints return "Redirecting..." +- [ ] API endpoints timeout (exit code 28) +- [ ] Build logs don't mention API routes +- [ ] Framework Preset still shows "Other" + +--- + +## 🚨 Common Mistakes + +### Mistake 1: Wrong Root Directory Format +``` +✗ /apps/web/ (leading/trailing slashes) +✗ ./apps/web (relative path notation) +✗ apps/web/ (trailing slash) +✓ apps/web (correct) +``` + +### Mistake 2: Leaving Custom Build Commands +If you have: +```json +{ + "buildCommand": "cd ../.. && turbo build --filter=@tpmjs/web" +} +``` + +This MIGHT work, but can break Next.js detection. Start with empty and only add if needed. + +### Mistake 3: Not Removing Domain from Old Project +If `v0-tool-registry-page` still has `tpmjs.com`, your requests will route to the wrong project randomly based on: +- DNS propagation +- Edge cache +- Vercel's routing priority + +### Mistake 4: Not Verifying Framework Preset +"Other" is Vercel's default when it can't detect a framework. This is the #1 cause of missing API routes in monorepos. + +--- + +## 🔧 Troubleshooting + +### If API routes STILL don't deploy after all 4 steps: + +1. **Check Build Logs:** + - Does it say "Detected Next.js"? + - Does it list "route (app) /api/*"? + - Does it show `@vercel/next` builder? + +2. **Check package.json location:** + ``` + ✓ Should exist: apps/web/package.json + ✗ Should NOT be at root ONLY + ``` + +3. **Check next.config.ts location:** + ``` + ✓ Should exist: apps/web/next.config.ts + ``` + +4. **Verify pnpm workspace:** + ```bash + # Should show @tpmjs/web + pnpm list --depth 0 --filter @tpmjs/web + ``` + +5. **Test local build with Vercel CLI:** + ```bash + cd apps/web + vercel build --debug + ``` + Look for "Framework: nextjs" in output. + +--- + +## 📞 When to Contact Vercel Support + +If after completing all 4 steps: +- Build logs show "Detected Next.js" +- Build logs show "route (app) /api/*" +- BUT `vercel inspect` still doesn't list API functions + +Then you have a Vercel platform bug. Contact support with: +- This checklist +- Build logs +- `vercel inspect` output +- Link to `API_ROUTES_TIMEOUT_INVESTIGATION.md` + +--- + +## 🎯 Quick Win Test + +**Don't want to change production settings yet?** + +1. Create a NEW Vercel project +2. Import the SAME repo +3. Set Root Directory to `apps/web` +4. Set Framework Preset to "Next.js" +5. Deploy + +If API routes work in the new project → confirms the fix +If API routes still fail → deeper issue (contact support) + +--- + +## ✨ Post-Fix Cleanup + +Once API routes are working: + +### Optional: Re-add www redirect + +Now that API routes work, you can safely add the www redirect back: + +**Option A - Vercel Project Settings:** +Vercel Dashboard → Domains → tpmjs.com → Redirect www to apex + +**Option B - Next.js config:** +```typescript +// apps/web/next.config.ts +async redirects() { + return [ + { + source: '/:path((?!api).*)*', // Exclude /api/* + has: [{ type: 'host', value: 'www.tpmjs.com' }], + destination: 'https://tpmjs.com/:path*', + permanent: true, + }, + ]; +} +``` + +**Option C - vercel.json (not recommended):** +Only use if you understand the implications. + +### Optional: Remove maxDuration exports + +The `export const maxDuration = 60;` in your route files isn't needed unless you actually need longer timeouts. Default is 10s (Hobby) or 15s (Pro). + +--- + +## 📊 Summary + +| Issue | Root Cause | Fix | +|-------|------------|-----| +| API routes timeout | Vercel doesn't detect Next.js | Set Framework Preset to "Next.js" | +| No λ functions in build | Wrong Root Directory | Set to `apps/web` exactly | +| "Redirecting..." on API calls | Domain on two projects | Remove from old project | +| Build doesn't find API routes | Custom build commands break detection | Clear custom commands | + +**Time to fix:** 5 minutes (just changing dashboard settings) +**Deployments needed:** 1 (changes take effect on next deploy) +**Code changes needed:** 0 (this is pure configuration) + +--- + +## 🎉 When It Works + +You'll know it's fixed when: + +```bash +$ curl https://tpmjs.com/api/health +{"status":"ok","timestamp":"2025-11-28T...","env":{"hasDatabase":true,"nodeEnv":"production"}} + +$ curl https://tpmjs.com/api/tools +{"data":[...],"pagination":{...}} +``` + +And `vercel inspect ` shows: +``` +Builds + ├── λ api/health + ├── λ api/tools + ├── λ api/stats + ... (ALL your API routes) +``` + +**That's it. No code changes. Just fix the Vercel project configuration.** diff --git a/apps/web/src/app/api/tools/route.ts b/apps/web/src/app/api/tools/route.ts index aae68cd..abeab4d 100644 --- a/apps/web/src/app/api/tools/route.ts +++ b/apps/web/src/app/api/tools/route.ts @@ -64,11 +64,7 @@ export async function GET(request: NextRequest) { // For first page, we can skip count if we don't need total pages const tools = await prisma.tool.findMany({ where, - orderBy: [ - { qualityScore: 'desc' }, - { npmDownloadsLastMonth: 'desc' }, - { createdAt: 'desc' }, - ], + orderBy: [{ qualityScore: 'desc' }, { npmDownloadsLastMonth: 'desc' }, { createdAt: 'desc' }], take: limit + 1, // Fetch one extra to check if there are more skip: offset, }); diff --git a/package.json b/package.json index 5462ec2..5bd7584 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "changeset": "changeset", "changeset:version": "changeset version && pnpm install --no-frozen-lockfile", "changeset:publish": "pnpm build && changeset publish", - "prepare": "lefthook install" + "prepare": "node -e \"if (require('fs').existsSync('.git')) { require('child_process').execSync('lefthook install', {stdio: 'inherit'}) }\"" }, "devDependencies": { "@biomejs/biome": "^1.9.4", diff --git a/packages/db/test-query.mjs b/packages/db/test-query.mjs new file mode 100644 index 0000000..0aa9b63 --- /dev/null +++ b/packages/db/test-query.mjs @@ -0,0 +1,38 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main() { + console.log('Testing database connection...'); + + // Test 1: Count tools + console.time('Count tools'); + const count = await prisma.tool.count(); + console.timeEnd('Count tools'); + console.log(`Total tools: ${count}`); + + // Test 2: Fetch first 20 tools (same as API) + console.time('Fetch 20 tools'); + const tools = await prisma.tool.findMany({ + orderBy: [{ qualityScore: 'desc' }, { npmDownloadsLastMonth: 'desc' }, { createdAt: 'desc' }], + take: 20, + }); + console.timeEnd('Fetch 20 tools'); + console.log(`Fetched ${tools.length} tools`); + + if (tools.length > 0) { + console.log('\nFirst tool:', tools[0].npmPackageName); + console.log('Quality score:', tools[0].qualityScore); + } else { + console.log('\n⚠️ No tools found in database!'); + } +} + +main() + .catch((e) => { + console.error('❌ Database error:', e.message); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/test-db.mjs b/test-db.mjs new file mode 100644 index 0000000..b73c596 --- /dev/null +++ b/test-db.mjs @@ -0,0 +1,40 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient({ + datasources: { + db: { + url: process.env.DATABASE_URL, + }, + }, +}); + +async function main() { + console.log('Testing database connection...'); + + // Test 1: Count tools + console.time('Count tools'); + const count = await prisma.tool.count(); + console.timeEnd('Count tools'); + console.log(`Total tools: ${count}`); + + // Test 2: Fetch first 20 tools (same as API) + console.time('Fetch 20 tools'); + const tools = await prisma.tool.findMany({ + orderBy: [{ qualityScore: 'desc' }, { npmDownloadsLastMonth: 'desc' }, { createdAt: 'desc' }], + take: 20, + }); + console.timeEnd('Fetch 20 tools'); + console.log(`Fetched ${tools.length} tools`); + + // Test 3: Check if there are indexes + console.log('\nFirst tool:', tools[0]?.npmPackageName || 'No tools found'); +} + +main() + .catch((e) => { + console.error('Database error:', e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });