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
| One route model | Contracts stay connected | Start with only what you need |
|---|---|---|
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. |
# Package name is vextjs — runs the published `vext` binary from this package.
npx vextjs create my-app
cd my-app
npm run devDefault scaffold: TypeScript + fullstack React + native adapter.
# API-only
npx vextjs create my-api --template api --frontend none
# Other adapters / JS
npx vextjs create my-app --adapter hono
npx vextjs create my-app --jsOpen 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.
| When the application grows | VextJS keeps | Practical result |
|---|---|---|
| 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 |
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.
- 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.jsonand interactive first-party/docs - Database lifecycle — setting
config.databaseactivates 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
- 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.tswhen frontend build +apiClientare enabled) - Vext JSCSS (
vextjs/style) and esbuild-powered frontend delivery — one toolchain
- 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 —
createTestAppfromvextjs/testingwithout binding a real HTTP port
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
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.
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.
Pick the stack that fits your deployment; business routes stay the same.
| Adapter | Extra packages | Notes |
|---|---|---|
| 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 |
// src/config/default.js
export default {
adapter: "native", // or "fastify" | "hono" | "express" | "koa"
};Benchmark methodology and numbers: Benchmark docs · reproduce with npm run test:bench in this repo.
Invoke via npx vextjs <cmd> (package) or, inside a project, npx vext <cmd> / npm scripts.
| Command | Purpose |
|---|---|
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 # → vext dev
npm run build # → vext build
npm start # → vext startDev reload: T1/T2 soft reload (ms), T3 cold restart (config/plugins/env). Keys: r restart, h soft reload, c clear, ? help.
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.
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);
});
});| Resource | URL |
|---|---|
| Human docs (EN/ZH) | https://devcodex-labs.github.io/vextjs/ |
| Quick start | https://devcodex-labs.github.io/vextjs/guide/quick-start |
| Frontend guide and typed client | https://devcodex-labs.github.io/vextjs/frontend/getting-started |
| Runtime boundaries | https://devcodex-labs.github.io/vextjs/frontend/boundaries-and-roadmap |
llms.txt |
https://devcodex-labs.github.io/vextjs/llms.txt |
capabilities.json |
https://devcodex-labs.github.io/vextjs/capabilities.json |
docs-manifest.json |
https://devcodex-labs.github.io/vextjs/docs-manifest.json |
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).
Schema-dsl v3 / monsqlize / frontend contracts: MIGRATION.md.
See the contributing guide.
git clone https://github.com/devcodex-labs/vextjs.git
cd vextjs
npm ci
npm test
npm run buildApache-2.0 © DevCodex Labs · github.com/devcodex-labs/vextjs