Fix: Vinxi Not Working — Dev Server Not Starting, Routes Not Matching, or Build Failing
Quick Answer
How to fix Vinxi server framework issues — app configuration, routers, server functions, middleware, static assets, and deployment to different platforms.
The Problem
The Vinxi dev server fails to start:
npx vinxi dev
# Error: No app config found — or —
# Error: Cannot find module 'vinxi'Or routes return 404:
GET /api/users → 404 Not FoundOr server and client code mix incorrectly:
ReferenceError: process is not defined (in client bundle)Why This Happens
Vinxi is a full-stack JavaScript SDK that powers frameworks like TanStack Start and SolidStart. It orchestrates multiple “routers” (server, client, SSR) in a single application:
- Vinxi needs an
app.config.js— the configuration file defines routers, their modes (server, client, SSR), and how they compose together. Without it, Vinxi has nothing to build. - Routers have different capabilities — a
"server"router handles API routes and server functions. A"client"router bundles client-side JavaScript. An"ssr"router renders on the server and hydrates on the client. Mixing code between router types causes runtime errors. - File-system routing depends on the router configuration — each router can have its own
dirfor file-based routing. Files outside the configured directory aren’t recognized as routes. - Vinxi uses Nitro under the hood — the server runtime comes from Nitro (same as Nuxt). Server configuration, presets, and deployment targets follow Nitro conventions.
Fix 1: Basic Vinxi App
npm install vinxi// app.config.js
import { createApp } from 'vinxi';
export default createApp({
routers: [
// Static file server (public/)
{
name: 'public',
type: 'static',
dir: './public',
},
// API server routes
{
name: 'api',
type: 'http',
handler: './app/api.ts',
base: '/api',
},
// Client-side SPA
{
name: 'client',
type: 'spa',
handler: './app/client.tsx',
target: 'browser',
base: '/',
plugins: () => [/* vite plugins */],
},
],
});// app/api.ts — server handler
import { eventHandler, getQuery, readBody, createRouter, defineEventHandler } from 'vinxi/http';
const router = createRouter();
router.get('/users', defineEventHandler(async (event) => {
const query = getQuery(event);
const users = await db.query.users.findMany({
limit: Number(query.limit) || 20,
});
return users;
}));
router.post('/users', defineEventHandler(async (event) => {
const body = await readBody(event);
const [user] = await db.insert(users).values(body).returning();
return user;
}));
export default router.handler;// app/client.tsx — client entry
import { createRoot } from 'react-dom/client';
function App() {
return <div>Hello from Vinxi!</div>;
}
createRoot(document.getElementById('root')!).render(<App />);Fix 2: SSR Application
// app.config.js — full SSR setup
import { createApp } from 'vinxi';
import react from '@vitejs/plugin-react';
export default createApp({
routers: [
{
name: 'public',
type: 'static',
dir: './public',
},
{
name: 'api',
type: 'http',
base: '/api',
handler: './app/api.ts',
},
{
name: 'ssr',
type: 'http',
handler: './app/ssr.tsx',
target: 'server',
plugins: () => [react()],
},
{
name: 'client',
type: 'client',
handler: './app/client.tsx',
target: 'browser',
base: '/_build',
plugins: () => [react()],
},
],
});// app/ssr.tsx — server-side rendering handler
import { eventHandler } from 'vinxi/http';
import { renderToString } from 'react-dom/server';
import { getManifest } from 'vinxi/manifest';
export default eventHandler(async (event) => {
const clientManifest = getManifest('client');
const assets = await clientManifest.inputs[clientManifest.handler].assets();
const html = renderToString(<App />);
return `<!DOCTYPE html>
<html>
<head>
${assets.map(asset =>
asset.tag === 'script'
? `<script src="${asset.attrs.src}" ${asset.attrs.type ? `type="${asset.attrs.type}"` : ''}></script>`
: asset.tag === 'link'
? `<link rel="${asset.attrs.rel}" href="${asset.attrs.href}" />`
: ''
).join('\n')}
</head>
<body>
<div id="root">${html}</div>
</body>
</html>`;
});Fix 3: File-System Routing
// app.config.js — file-based API routes
import { createApp } from 'vinxi';
export default createApp({
routers: [
{
name: 'api',
type: 'http',
base: '/api',
dir: './app/api', // Directory-based routing
handler: './app/api/_handler.ts',
routes: (router, app) =>
new FileSystemRouter({
dir: './app/api',
style: 'nextjs', // [param] style routes
}),
},
],
});app/api/
├── _handler.ts # Catch-all handler
├── users/
│ ├── index.get.ts # GET /api/users
│ ├── index.post.ts # POST /api/users
│ └── [id].get.ts # GET /api/users/:id
└── health.get.ts # GET /api/health// app/api/users/index.get.ts
import { eventHandler } from 'vinxi/http';
export default eventHandler(async () => {
return db.query.users.findMany();
});
// app/api/users/[id].get.ts
import { eventHandler, getRouterParam } from 'vinxi/http';
export default eventHandler(async (event) => {
const id = getRouterParam(event, 'id');
const user = await db.query.users.findFirst({ where: eq(users.id, id) });
if (!user) throw createError({ statusCode: 404, message: 'Not found' });
return user;
});Fix 4: Middleware
// app/middleware.ts
import { eventHandler, getCookie, setCookie } from 'vinxi/http';
// CORS middleware
export const corsMiddleware = eventHandler((event) => {
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
});
if (event.method === 'OPTIONS') {
setResponseStatus(event, 204);
return '';
}
});
// Auth middleware
export const authMiddleware = eventHandler(async (event) => {
const token = getRequestHeader(event, 'authorization')?.replace('Bearer ', '');
if (!token) {
throw createError({ statusCode: 401, message: 'Unauthorized' });
}
const user = await verifyToken(token);
event.context.user = user;
});
// Apply in app config
export default createApp({
routers: [
{
name: 'api',
type: 'http',
base: '/api',
handler: './app/api.ts',
middleware: './app/middleware.ts', // Applied to all routes in this router
},
],
});Fix 5: Server Functions
// Vinxi supports server functions (used by TanStack Start, SolidStart)
// These compile to RPC endpoints automatically
// app/server/functions.ts
'use server';
export async function getUsers() {
// This code only runs on the server
return db.query.users.findMany();
}
export async function createUser(name: string, email: string) {
const [user] = await db.insert(users).values({ name, email }).returning();
return user;
}
// Called from client code — transparently makes HTTP request
import { getUsers, createUser } from './server/functions';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
getUsers().then(setUsers);
}, []);
async function handleCreate() {
const user = await createUser('Alice', '[email protected]');
setUsers(prev => [...prev, user]);
}
return (/* ... */);
}Fix 6: Deployment
# Build for production
npx vinxi build
# The output depends on the server preset
# Default: .output/ directory with Nitro server
# Run production server
node .output/server/index.mjs// app.config.js — deployment presets
export default createApp({
server: {
preset: 'node-server', // Standard Node.js
// preset: 'vercel', // Vercel
// preset: 'netlify', // Netlify
// preset: 'cloudflare-pages', // Cloudflare
// preset: 'bun', // Bun runtime
},
routers: [/* ... */],
});Still Not Working?
“No app config found” — Vinxi looks for app.config.js (or .ts) in the project root. If using TanStack Start or SolidStart, the config file may be named differently (app.config.ts with framework-specific defineConfig).
Routes return 404 — check the base path. If a router has base: '/api', routes inside it are prefixed with /api. Also verify the handler file exists and exports a valid handler.
Client code imports server modules — Vinxi separates server and client bundles. Importing a module that uses fs, db, or other server APIs in client code causes “module not found” errors. Use server functions or the 'use server' directive.
Build fails with Vite errors — Vinxi uses Vite internally. Plugin compatibility issues (React, CSS) show as Vite errors. Check that plugins are returned from the plugins() function, not set directly as an array.
For related framework issues, see Fix: TanStack Start Not Working and Fix: Nitro Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Deno PermissionDenied — Missing --allow-read, --allow-net, and Other Flags
How to fix Deno PermissionDenied (NotCapable in Deno 2) errors — the right permission flags, path-scoped permissions, deno.json permission sets, and the Deno.permissions API.
Fix: Fastify Not Working — 404, Plugin Encapsulation, and Schema Validation Errors
How to fix Fastify issues — route 404 from plugin encapsulation, reply already sent, FST_ERR_VALIDATION, request.body undefined, @fastify/cors, hooks not running, and TypeScript type inference.
Fix: Analog Not Working — Routes Not Loading, API Endpoints Failing, or Vite Build Errors
How to fix Analog (Angular meta-framework) issues — file-based routing, API routes with Nitro, content collections, server-side rendering, markdown pages, and deployment.
Fix: Angular SSR Not Working — Hydration Failing, Window Not Defined, or Build Errors
How to fix Angular Server-Side Rendering issues — @angular/ssr setup, hydration, platform detection, transfer state, route-level rendering, and deployment configuration.