VextJS

Ship APIs and server-rendered React pages from one Node.js application.
VextJS is a full-stack Node.js application framework built around one route model and request lifecycle. Routes, services, validation, security, OpenAPI, typed contracts, and React SSR evolve together without introducing a second routing system.
The npm package name is vextjs; the CLI binary is vext. Requires Node.js >=20.19.0. Cold-start from the registry with npx vextjs …. After install, use project scripts or local npx vext.
Docs: https://devcodex-labs.github.io/vextjs/ · Migration: MIGRATION.md · Changelog: CHANGELOG.md
JSON and HTML use src/routes/**, shared services, and the same security chain. | Route contracts drive validation, OpenAPI, interactive docs, and generated client types. | The default starter is full-stack; API-only remains a first-class template. |
Get started
npx vextjs create my-app
cd my-app
npm run dev
Default scaffold: TypeScript + fullstack React + native adapter.
npx vextjs create my-api --template api --frontend none
npx vextjs create my-app --adapter hono
npx vextjs create my-app --js
Open http://localhost:3000/ and, with OpenAPI enabled, http://localhost:3000/docs.
The package is vextjs; its installed CLI binary is vext. Use npx vextjs create before installation, then project scripts or local npx vext.
Why VextJS
| An API also needs server-rendered pages | URL ownership in src/routes/** | No second route or data-loading language |
| Validation, docs, and clients start to drift | Contracts beside the handler | OpenAPI, live docs, and typed clients share one source |
| Authentication, sessions, cache, and errors multiply | One request lifecycle | JSON, HTML, and page navigation cross the same policies |
| Tooling becomes a project of its own | CLI, esbuild frontend delivery, testing, and production start | A shorter path from scaffold to deployable Node service |
One route model
The default starter demonstrates the same service feeding a server-rendered page and a documented API route:
import { defineRoutes } from "vextjs";
export default defineRoutes((app) => {
app.get("/", {}, async (_req, res) => {
const greeting = await app.services.example.greeting("Vext");
res.render("index", { greeting, renderedAt: new Date().toISOString() });
});
app.get(
"/api/hello",
{
docs: { summary: "Get the starter greeting" },
},
async (_req, res) => {
res.json(await app.services.example.greeting("Vext"));
},
);
});
Routes remain the URL authority. Services own reusable business work. res.json() and res.render() choose the representation without bypassing middleware, validation, auth, cache, redirects, or error handling.
What you get
Runtime
- Convention routing — file path under
src/routes/** → URL prefix; (path, options, handler) API
- Plugins & services — topological plugin load;
src/services/** → app.services
- Validation & route contracts — schema-dsl, response schemas, OpenAPI, and generated client types
- OpenAPI + Docs Renderer —
/openapi.json and interactive first-party /docs
- Database lifecycle — setting
config.database activates built-in MonSQLize connections, model loading, and cleanup
- Route cache —
cache: 60 / tags / Vary
- Session, cookies, CSRF, auth, security headers — first-party contracts
- Production lifecycle — graceful shutdown, cluster workers, heartbeats, and rolling restart
- Startup lifecycle — bootstrap configuration providers and
src/preload/** process-early hooks
Full-stack UI (same routes)
- React SSR / hydration via
res.render()
- Same-route client navigation — browser navigation reuses the existing server route lifecycle
- Static / revalidate freshness and local image/font pipeline
- Typed API client generation (
dist/client/api.generated.ts when frontend build + apiClient are enabled)
- Vext JSCSS (
vextjs/style) and esbuild-powered frontend delivery — one toolchain
DX
- CLI — create, dev, build, start, typegen, doctor
- Three-tier hot reload — route hot swap, service/model structural reload, and safe cold restart
- React Fast Refresh — frontend updates without restarting the backend runtime
- Testing —
createTestApp from vextjs/testing without binding a real HTTP port
Simplified project model
my-app/
├── src/
│ ├── config/ # default + env profiles + optional bootstrap
│ ├── routes/ # HTTP routes (URL authority)
│ ├── services/ # → app.services.*
│ ├── models/ # optional database models
│ ├── plugins/
│ ├── middlewares/
│ ├── frontend/ # pages, components, styles, assets
│ ├── preload/ # process-level early scripts
│ └── locales/
├── package.json
└── tsconfig.json
Good fit
VextJS is a strong fit when:
- a Node API needs server-rendered product, admin, or internal pages without splitting ownership;
- one team wants routes, services, validation, auth, cache, docs, and clients to evolve together;
- API-only today may become full-stack later, or a full-stack app still needs a first-class API;
- production remains a Node service and an esbuild-based frontend toolchain is enough.
Boundaries
VextJS uses route-native SSR: src/routes/** + res.render() provide SSR, hydration, Suspense, opt-in Streaming SSR, same-route navigation, static/revalidate freshness, and local media. It intentionally does not implement React Server Components, Server Functions or Actions, partial prerendering (PPR), or third-party bundler plugin ecosystems. Those models would introduce a second execution or routing authority and weaken the framework's single-route contract.
Read the exact lifecycle, trade-offs, and exclusions in Frontend boundaries and roadmap. Implementation guides cover rendering modes, data flow, assets and media, and the typed API client.
HTTP adapters
Pick the stack that fits your deployment; business routes stay the same.
| native (default) | none | Node HTTP + route-core; no extra HTTP framework package |
express | express | Use when you need that middleware ecosystem |
fastify | fastify | Use when you need that plugin ecosystem |
koa | koa | Use when you need that middleware style |
hono | hono, @hono/node-server | Node.js adapter with Web Standard APIs; not an Edge runtime adapter |
export default {
adapter: "native",
};
Benchmark methodology and numbers: Benchmark docs · reproduce with npm run test:bench in this repo.
CLI
Invoke via npx vextjs <cmd> (package) or, inside a project, npx vext <cmd> / npm scripts.
vext create <name> | Scaffold (default fullstack-react) |
vext dev | Dev server + hot reload + typegen preflight |
vext build | Compile server (+ frontend when enabled) |
vext start | Run production / built output |
vext typegen | Generate app.services / app.extend types |
vext doctor routes | Static route diagnostics (experimental) |
npx vextjs create my-app
cd my-app
npm run dev
npm run build
npm start
Dev reload: T1/T2 soft reload (ms), T3 cold restart (config/plugins/env). Keys: r restart, h soft reload, c clear, ? help.
Configuration
built-in defaults → default → {profile} → local → bootstrap providers → CLI overrides
Select profile with vext start --config <name> or VEXT_CONFIG=<name> (do not rely on baked process.env.NODE_ENV after vext build for profile selection).
Common fields: port, host, adapter, logger, cors, bodyParser, rateLimit, openapi, frontend, cache, session, shutdown. See the configuration guide and configuration reference before changing production behavior.
Testing
import { describe, it, expect } from "vitest";
import { createTestApp } from "vextjs/testing";
describe("API", () => {
it("responds", async () => {
const app = await createTestApp({ rootDir: "/path/to/project" });
const res = await app.request.get("/");
expect(res.status).toBe(200);
});
});
Documentation and AI assistants
For AI assistants: prefer citing docs-manifest.json canonical URLs; check capabilities.json and the boundaries page before describing frontend capabilities. Do not invent features from React version, SSR, or Suspense alone.
Chinese documentation: site locale /zh (this package ships one English README entry).
Migration
Schema-dsl v3 / monsqlize / frontend contracts: MIGRATION.md.
Contributing
See the contributing guide.
git clone https://github.com/devcodex-labs/vextjs.git
cd vextjs
npm ci
npm test
npm run build
License
Apache-2.0 © DevCodex Labs · github.com/devcodex-labs/vextjs