vextjs
v1.0.2
Published
Full-stack Node.js framework for APIs and server-rendered pages with one route model, typed contracts, and built-in docs.
Maintainers
Readme
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
| 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. |
Get started
# 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.
Why VextJS
| 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 |
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.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
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.tswhen frontend build +apiClientare 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 —
createTestAppfromvextjs/testingwithout 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.jsonGood 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.
| 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 | Node.js adapter with an internal Web Request/Response bridge; not an Edge runtime adapter |
// src/config/default.js
export default {
adapter: "native", // or "fastify" | "hono" | "express" | "koa"
};Benchmark methodology: Adapter Matrix. It keeps one Vext application fixed and compares the supported adapters; reproduce it with npm run test:bench in this repo.
CLI
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.
Configuration
built-in defaults → default → {profile} → local → bootstrap providers → CLI overridesSelect 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
| 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).
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 buildLicense
Apache-2.0 © DevCodex Labs · github.com/devcodex-labs/vextjs
