--- name: vite-plugin-to-express description: Extract a Vite API plugin into a standalone Express production server. Use when deploying Vite apps that embed their API as a Vite plugin (not suitable for production). tags: [vite, express, deployment, api, production] --- # Vite Plugin API → Express Production Server ## Trigger - App has API routes embedded in a Vite plugin (e.g., `vite-plugin-api.ts`) - Need to deploy to production where Vite dev server won't run - API uses `server.middlewares.use()` or custom route handlers inside Vite plugin ## Steps 1. **Identify the Vite plugin API file** — usually registers routes via `configureServer` hook 2. **Create `server.js`** (or `server.ts`) standalone Express app: - Copy all route handlers from the Vite plugin - Replace Vite middleware patterns with Express `app.get/post/put/delete` - Add `express.static('dist')` to serve the built frontend - Add catch-all route for SPA: `app.get('/{*splat}', ...)` (Express 5) or `app.get('*', ...)` (Express 4) 3. **Build frontend**: `npm run build` → produces `dist/` 4. **Database connection**: Keep the same pg Pool config, just update host/port for production 5. **Run with PM2**: `pm2 start server.js --name app-name` ## Pitfalls - **Express 5 catch-all syntax**: Use `/{*splat}` NOT `*` — Express 5 rejects bare `*` wildcards - **tsx caching**: When using `tsx` to run TypeScript, kill the process fully and restart after file changes — tsx can cache old versions - **Port conflicts**: Check if Postgres 5432 is already in use; use alternate port (e.g., 5433) for Docker Postgres - **Static file path**: Use `path.join(__dirname, 'dist')` or resolve relative to server.js location ## Verification ```bash curl http://localhost:PORT/api/health # API responds curl http://localhost:PORT/ # Frontend HTML served ```