A Vite plugin that integrates srvx (Universal Server) with Vite's development server, similar to how @hono/vite-dev-server works with Hono.
- Automatic index.html serving - The plugin automatically serves
index.htmlon the root path - Hot Module Replacement (HMR) - Full HMR support for your srvx server
- Automatic Vite client script injection - Vite's dev client is automatically injected into HTML responses
- Web Standard APIs - Use Request/Response APIs that work everywhere
- Universal - Works with Node.js, Deno, and Bun
- Lightning fast - Powered by Vite's blazing fast dev server
- Vercel Edge Functions - Built-in support for deploying to Vercel (auto-detected!)
npm install vite-plugin-srvx srvx viteOr with pnpm:
pnpm add vite-plugin-srvx srvx viteCreate an index.html file in your project root:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My srvx App</title>
</head>
<body>
<h1>Hello from srvx + Vite!</h1>
<script type="module" src="/src/main.ts"></script>
</body>
</html>Create a src/server.ts file for your API routes:
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url)
// The plugin automatically serves index.html on '/'
// so you only need to handle API routes here
if (url.pathname === '/api/hello') {
return Response.json({
message: 'Hello from srvx!',
timestamp: new Date().toISOString(),
})
}
return new Response('Not Found', { status: 404 })
},
}Create a vite.config.ts file:
import { defineConfig } from 'vite'
import srvx from 'vite-plugin-srvx'
export default defineConfig(({ mode }) => ({
build: {
outDir: mode === 'server' ? 'dist' : 'dist/public',
},
plugins: [
...srvx({
entry: './src/server.ts',
}),
],
}))viteYour srvx server will now run with Vite's dev server! Visit http://localhost:5173 to see your app:
/- Automatically servesindex.html/api/hello- Your srvx API endpoint- All other routes are handled by your srvx server's fetch handler
The plugin uses Vite's mode system to handle client and server builds separately.
Add these scripts to your package.json:
{
"scripts": {
"dev": "vite",
"build": "npm run build:client && npm run build:server",
"build:client": "vite build",
"build:server": "vite build --mode server",
"preview": "srvx dist/server.js"
}
}Then build your app:
npm run buildThis will:
- Build your frontend (HTML, CSS, JS) to
dist/public - Build your srvx server to
dist/server.js
Run your production build:
npm run preview
# or directly: srvx dist/server.jssrvx automatically serves static files from the dist/public directory!
interface SrvxOptions {
// Entry file for your srvx server (default: './src/server.ts')
entry?: string
// Output directory for server build (default: 'dist')
outDir?: string
// Server output filename (default: 'server.js')
serverOutFile?: string
// Development server options
// Patterns to exclude from the srvx handler (will be handled by Vite instead)
exclude?: (string | RegExp)[]
// Whether to inject Vite's client script for HMR (default: true)
injectClientScript?: boolean
// Custom module loader (default: uses Vite's ssrLoadModule)
loadModule?: (server: ViteDevServer, entry: string) => Promise<any>
}Note: The plugin returns an array of two plugins (dev server + build), so use the spread operator:
...srvx({})
import { defineConfig } from 'vite'
import srvx from 'vite-plugin-srvx'
export default defineConfig(({ mode }) => ({
build: {
outDir: mode === 'server' ? 'build' : 'build/public',
},
plugins: [
...srvx({
entry: './src/server.ts',
outDir: 'build',
serverOutFile: 'app.js',
exclude: [
/.*\.tsx?$/,
/.*\.css$/,
/^\/@.+$/,
],
injectClientScript: true,
}),
],
}))Then build with:
npm run build:client # builds to build/public
npm run build:server # builds to build/app.jsAnd run: srvx build/app.js (it will serve static files from build/public)
If you need more control, you can import the plugins separately:
import { defineConfig } from 'vite'
import { devServer, srvxBuild } from 'vite-plugin-srvx'
export default defineConfig(({ mode }) => ({
build: {
outDir: mode === 'server' ? 'dist' : 'dist/public',
},
plugins: [
devServer({ entry: './src/server.ts' }),
srvxBuild({ entry: './src/server.ts' }),
],
}))The devServer plugin creates a Vite middleware that:
- Serves index.html on root - When requesting
/, the plugin automatically serves and transforms yourindex.htmlusing Vite'stransformIndexHtml(which handles script injection, etc.) - Intercepts other HTTP requests - All non-root requests are passed to your srvx server
- Loads your srvx server module - Uses Vite's SSR module loader for HMR support
- Converts to Web Standard APIs - Converts Node.js
IncomingMessage→ Web StandardRequest - Calls your fetch handler - Your srvx server's
fetchhandler processes the request - Converts the response - Converts the
Responseback to Node.jsServerResponse - Injects Vite client - For HTML responses from your server, Vite's client script is injected for HMR
The srvxBuild plugin uses Vite's mode system:
-
Client build (
vite build):- Builds frontend to
dist/public - Plugin is inactive (mode !== 'server')
- Builds frontend to
-
Server build (
vite build --mode server):- Plugin activates (mode === 'server')
- Sets
ssr: truevia theconfighook - Builds server to
dist/server.js
-
Run with srvx:
srvx dist/server.js- srvx automatically serves static files from
dist/public
This approach follows the same pattern as @hono/vite-build
This gives you the best of both worlds: srvx's universal server API and Vite's lightning-fast development experience!
Check out the example directory for a full working example.
To run the example:
pnpm install
pnpm build
cd example
pnpm devThis plugin is heavily inspired by @hono/vite-dev-server but designed specifically for srvx:
- Similar middleware architecture
- Same HMR capabilities
- Compatible API design
- Works with any framework that uses Web Standard fetch API
MIT
Inspired by @hono/vite-dev-server