Skip to main content

Deployment

Kinetic Email is hosted on Vercel with automatic deployments from the main branch — push to main and Vercel builds and deploys.

Build Pipeline

npx convex deploy \
--cmd 'npm run build:vite && npm run build:prerender && npm run build:sitemap' \
--cmd-url-env-var-name VITE_CONVEX_URL --codegen enable -y

This runs in sequence:

  1. Convex deploy — Deploys real-time tracking functions (requires CONVEX_DEPLOY_KEY in Vercel) and injects VITE_CONVEX_URL for the frontend build
  2. Vite build — Compiles React app to dist/
  3. Pre-render — Generates static HTML for SEO (scripts/prerender.mjs)
  4. Sitemap — Generates dist/sitemap.xml (scripts/generate-sitemap.mjs)

Vercel Configuration

The vercel.json file defines:

  • Framework: Vite
  • API functions: Serverless functions from api/ directory
  • Rewrites: SPA fallback (/*index.html), blog OG images
  • Headers: Security headers (X-Content-Type-Options, X-Frame-Options), CORS, cache control
  • CORS: Restricted to kinetic.email and localhost origins

Pre-rendering for SEO

The pre-render script (scripts/prerender.mjs) injects static HTML and meta tags into route-specific index.html files:

dist/index.html              → Homepage
dist/learn/index.html → Learning hub
dist/playground/index.html → AI Playground
dist/blog/index.html → Blog listing
...

SEO metadata is configured in src/seo/seoConfig.ts. Auth-gated pages (learn modules 2-6, marketing modules 2-5) have noindex: true.

Running Tests Before Deploy

npm test    # Run all tests
npm run build # Verify build succeeds

Always run tests before deploying, especially after modifying:

  • API routes
  • Auth logic
  • Token economy functions
  • Email generation pipeline

Smoke-Testing api/ After Deploy

Vitest is not Vercel — always verify api/ changes against the real runtime after deploying:

curl -s -X POST https://kinetic.email/api/generate
  • 401 JSON response — healthy: the module loaded and auth rejected the unauthenticated request
  • Plain-text "A server error has occurred" — the function crashed at module load (FUNCTION_INVOCATION_FAILED)

The most common module-load crash: api/ runs as ESM, so relative imports must carry the .js extension ('./_auth.js', even for .ts sources). Extensionless imports pass Vitest locally but crash every invocation on Vercel.

Also note: migrations that add columns read by deployed client queries must run in Supabase before the deploy — a select() naming a missing column returns a 400.

Environment Variables

All environment variables must be configured in the Vercel project settings. See Environment Variables for the complete list.