NNextron Docs

Express backend

The Vite + Node stack — a modular Express + TypeScript API with MongoDB or SQL Server, JWT auth, AI, tRPC, jobs, payments, and an optional Vite React frontend.

The Vite + Node stack generates an Express + TypeScript backend with strict typing, zod validation, and a modular folder layout. Choose it at the first nextron create prompt.

Layouts

  • API only — the Express backend at the project root.
  • Monorepo — a pnpm workspace with apps/api (the same Express backend) and apps/web (Vite + React + TypeScript + Tailwind CSS v4). The web dev server proxies /api/* to the backend, so no CORS setup is needed in development.

Web app architecture (monorepo)

The apps/web frontend follows the same separation of concerns as the backend, so components stay thin and API access stays in one place:

apps/web/src/
  main.tsx                 # RouterProvider entrypoint
  router.tsx               # react-router-dom routes (modules inject here)
  shared/
    lib/api.ts             # the HTTP transport — ONLY services import this
    components/Layout.tsx   # app shell + nav (modules inject links)
  modules/
    <feature>/
      services/            # all API calls for the feature live here
      pages/               # routed pages — call services, never `api` directly
      components/          # feature UI
      store/               # feature state (e.g. auth)
  • Never call api() from a component. Components and pages call a service (modules/<feature>/services/*.service.ts); only services import the api transport. This keeps request/response shapes, endpoints, and error handling in one testable layer.
  • Routing is react-router-dom. Modules register their pages at the nextron:web-route-imports / nextron:web-routes markers in router.tsx and their nav links at the nextron:web-nav marker in Layout.tsx — the same marker mechanism the backend uses.
  • The @/ alias maps to apps/web/src (configured in vite.config.ts and tsconfig.json).
  • Auth wires a token provider into the transport, so every request automatically carries Authorization: Bearer <token> once signed in — services never set it by hand.

Project structure

src/
  @types/         # global types + Express Request augmentation (rawBody)
  config/         # env.ts (zod-validated), db.ts, payment clients
  controllers/    # thin request handlers
  enums/          # shared enums (HTTP status, ...)
  middlewares/    # error handler, zod validation middleware
  models/         # Mongoose models or SQL row types + bootstrap SQL
  routes/         # routers, aggregated in routes/index.ts
  seeders/        # `pnpm seed` entrypoint + seeders
  services/       # business logic — the only layer that touches the database
  utils/          # asyncHandler, logger
  validations/    # zod schemas + inferred input types
  index.ts        # bootstrap: env → db → express → graceful shutdown

Conventions the generated code follows (and that AGENTS.md teaches your coding agent):

  • Env vars are accessed only through src/config/env.ts, validated with zod at startup.
  • Controllers stay thin; business logic lives in src/services/.
  • Every request is validated with the validate middleware and schemas from src/validations/.
  • New routers are registered in src/routes/index.ts between the // nextron:route-imports and // nextron:route-mounts markers — nextron add uses the same markers.
  • Operational errors throw AppError; the central error middleware formats every response.

Databases

ChoiceWhat you get
MongoDBMongoose connection with lifecycle events, a User model, service, and seeder
SQL Servermssql connection pool, a typed query<T>(sql, params) helper, parameterized SQL service, and a seeder that bootstraps the users table

Both flavors expose the same service surface (findAll, findById, create, update, remove), so controllers and routes are identical either way.

pnpm seed   # seeds sample users (creates the table first on SQL Server)

Authentication

Choose JWT at the auth prompt (or add it later with nextron add express-auth) to scaffold email/password authentication. It requires a database module — credentials are stored on the same users store, with a hidden passwordHash field/column added automatically.

EndpointPurpose
POST /api/auth/registerCreate a user ({ name, email, password }) → { token, user }
POST /api/auth/loginVerify credentials ({ email, password }) → { token, user }
GET /api/auth/meReturn the current user — requires Authorization: Bearer <token>

Passwords are hashed with bcrypt; tokens are signed with JWT_SECRET and expire after JWT_EXPIRES_IN (default 7d). Protect any route by adding the authenticate middleware — it verifies the Bearer token and attaches the typed user to req.user.

In a monorepo, the web app also gets an auth service, a reactive store (useAuth), /login and /register pages, and a /dashboard guarded by <ProtectedRoute> — all wired into the router automatically.

import { authenticate } from '../middlewares/auth.middleware.js';

router.get('/me/orders', authenticate, listMyOrders);

Payments

Select Stripe and/or Polar during setup, or add them later from the API root:

npx @edwinfom/nextron@latest add stripe-express
npx @edwinfom/nextron@latest add polar-express
EndpointModulePurpose
POST /api/payments/stripe/checkoutstripe-expressCreate a Stripe Checkout session
POST /api/payments/stripe/webhookstripe-expressSignature-verified Stripe webhook
POST /api/payments/polar/checkoutpolar-expressCreate a Polar checkout
POST /api/payments/polar/portalpolar-expressCreate a customer-portal session
POST /api/payments/polar/webhookpolar-expressSignature-verified Polar webhook

Webhook signature verification relies on the raw request body, which the base template captures as req.rawBody in src/index.ts.

Other modules

Pick these at the "Additional modules" prompt, or add them later with nextron add <module>:

ModuleAdds
express-aiA streaming POST /api/ai/chat endpoint on the Vercel AI SDK. Choose the provider (Anthropic, OpenAI, DeepSeek, Google) at setup; it speaks the AI SDK data-stream protocol, so useChat works on the frontend.
express-trpcA tRPC v11 router mounted at /api/trpc via the Express adapter. Export AppRouter and import its type in the web app for an end-to-end typed client.
express-inngestInngest's Express handler at /api/inngest plus an example durable function. Run npx inngest-cli@latest dev for the local dev server.

Each module registers itself at the nextron: route markers in src/routes/index.ts, adds only the packages and env vars it needs, and follows the same controller/service/validation layout as the rest of the backend.

In a monorepo these modules also add their frontend counterparts to apps/web: express-ai adds a /chat page (built on the AI SDK's useChat), and each payment module adds a checkout service + page (/checkout/stripe, /checkout/polar) — routes and nav links are injected automatically.

Commands

pnpm dev         # tsx watch (monorepo: runs api + web in parallel)
pnpm build       # tsc → dist/
pnpm start       # node dist/index.js
pnpm typecheck   # tsc --noEmit
pnpm seed        # seed sample data

Adding modules

Modules are stack-aware: Next.js modules (auth, trpc, dashboard, ...) cannot be added to an Express project, and *-express modules cannot be added to a Next.js project. In a monorepo, run nextron add from apps/api, where nextron.config.ts lives.

On this page