ExisJS

Introduction

ExisJS is an opinionated web framework for building TypeScript backends on Node.js. It brings convention-over-configuration and automatic file-system routing to backend API development.

Rather than manually assembling, wiring, and maintaining separate third-party libraries for routing, input validation, dependency injection, OpenAPI documentation, rate limiting, and background task scheduling, ExisJS provides an integrated standard library in a lightweight, pure TypeScript core with no native add-ons.


Why ExisJS?

TypeScript backend development often faces two approaches:

  1. Micro-frameworks (Express, Fastify): Require developers to manually select, wire, and maintain middleware packages for validation, OpenAPI schemas, dependency injection, and security headers. Over time, projects diverge in structure and maintenance overhead increases.
  2. Heavy enterprise frameworks: Introduce deep class hierarchies, verbose configuration modules, and steep learning curves.

ExisJS combines convention-based file routing, built-in standard modules, and a lean runtime with support for both functional and object-oriented paradigms.


Architectural Pillars

┌─────────────────────────────────────────────────────────────────────────────┐│                             ExisJS Application                              ││         Functional Paradigm         │        Class-Based (OOP) Paradigm     ││   controller(), route.get(), tex    │   @Controller(), @Get(), @Injectable  │└──────────────────────────────────────┬──────────────────────────────────────┘                                       │┌──────────────────────────────────────▼──────────────────────────────────────┐│                          TypeScript HTTP Pipeline                           ││  Request Context (ExisRequest/Response) · Hierarchical Boundaries · Swagger ││        Rate Limiting · Helmet Headers · CSRF · Idempotency · Telemetry      │└──────────────────────────────────────┬──────────────────────────────────────┘                                       │┌──────────────────────────────────────▼──────────────────────────────────────┐│                               Runtime Core                                  ││      Radix-Tree Router   │   Compiled tex Validators   │   CIDR IP Filter   ││      Secure Body Parser  │   Bounded In-Memory Caches                       │└─────────────────────────────────────────────────────────────────────────────┘

1. File-System Routing

The file-system routing engine scans the src/http directory and compiles route paths into a radix tree at startup. Purely static paths resolve through an exact-match table; parameterized paths walk the tree with no per-request parsing of the route definitions.

2. Dual-Paradigm Parity

ExisJS supports a functional and a class-based (OOP) paradigm. Both share the router, validation, boundaries, error handling, response handling and route authorization; a project uses one of them throughout (exisjs dev and exisjs build stop with the file names if they are mixed). Class-based controllers add decorator and dependency-injection work per request, so functional routes are somewhat faster.

  • Functional: controller(), route.get(), route.post(), defineBoundary(), inject().
  • Class-Based (OOP): @Controller(), @Get(), @Post(), @Boundary(), @Injectable().

3. Integrated Schema Validation & OpenAPI 3.1

The built-in tex schema builder provides runtime type validation, parameter coercion, and automated OpenAPI 3.1 schema generation. Every schema defined on a route automatically surfaces in the interactive Swagger UI (/docs) and exports types for compile-time frontend client generation.

4. Hierarchical Security & Error Boundaries

Directory-based boundary.ts files allow you to declare scoped authentication guards, rate limits, CORS configurations, and error handlers that cascade down folder trees without manual middleware repetition.

5. No Native Dependencies

The core is pure TypeScript. There are no platform binaries to download or compile, so ExisJS installs the same way on every OS and CPU architecture that Node.js supports.


Feature Matrix

Feature AreaExisJS Built-In CapabilityBenefit
RoutingFile-system route discovery (src/http/**/route.ts)No manual router mounting or path strings
Validationtex Schema Validator (compiled once per schema)Fast parsing, zero-crash sanitization, automatic OpenAPI types
DocumentationAutomatic Swagger / OpenAPI 3.1 at /docsSelf-updating interactive documentation with zero manual sync
Traffic ControlNative Token Bucket Rate Limiting & CIDR FilteringFast DoS prevention and client throttling
SecuritySecurity headers (helmet), CSRF, HPP, Body SanitizationSecure defaults without installing separate packages
Dependency InjectionType-safe IoC container with circular ref resolutionClean service decoupling with @Injectable() and inject()
Background TasksIn-process cron scheduler with drift correctionScheduled jobs without external cron infrastructure
ObservabilityNative OpenTelemetry integration (exisjs/telemetry)Standardized traces, metrics, and structured Pino logging

Quickstart Comparison

ExisJS allows you to choose the paradigm that best matches your team's conventions.

Functional Paradigm

src/http/users/route.ts
import { controller, route } from 'exisjs/router'import { tex } from 'exisjs/validator'
// 1. Define runtime validation schemaconst CreateUserSchema = tex.object({  name: tex.string().min(2),  email: tex.string().email(),  role: tex.enum(['admin', 'member']).default('member'),})
// 2. Export functional controllerexport default controller({  // GET /users  listUsers: route.get('/', {    summary: 'List all active users',    handle: async () => {      return [        { id: 1, name: 'Alice', role: 'admin' },        { id: 2, name: 'Bob', role: 'member' },      ]    },  }),
  // POST /users  createUser: route.post('/', {    summary: 'Create a new user',    body: CreateUserSchema,    handle: async ({ body }) => {      return {        id: Date.now(),        name: body.name,        email: body.email,        role: body.role,      }    },  }),})

Class-Based (OOP) Paradigm

src/http/users/route.ts
import { Controller, Get, Post, Body, ApiOperation } from 'exisjs/decorators'import { tex } from 'exisjs/validator'
// 1. Define runtime validation schema & inferred typeconst CreateUserSchema = tex.object({  name: tex.string().min(2),  email: tex.string().email(),  role: tex.enum(['admin', 'member']).default('member'),})
type CreateUserDto = typeof CreateUserSchema.infer
// 2. Export class controller@Controller()export default class UsersController {  @Get('/')  @ApiOperation({ summary: 'List all active users' })  async listUsers() {    return [      { id: 1, name: 'Alice', role: 'admin' },      { id: 2, name: 'Bob', role: 'member' },    ]  }
  @Post('/')  @ApiOperation({ summary: 'Create a new user' })  async createUser(@Body(CreateUserSchema) body: CreateUserDto) {    return {      id: Date.now(),      name: body.name,      email: body.email,      role: body.role,    }  }}

The Request Lifecycle

Every HTTP request dispatched through ExisJS follows a predictable, high-performance execution lifecycle:

Incoming Request      │      ▼1. Radix Path Match      │      ▼2. CIDR IP Filter & Rate Limit Check      │      ▼3. Root Boundary (Global Middleware & CORS)      │      ▼4. Nested Folder Boundaries (Scoped Guards & Interceptors)      │      ▼5. Request Body Parsing & TexValidation      │      ▼6. Controller Handler Execution (Functional or Class Method)      │      ▼7. Response Envelope / Stream Serialization      │      ▼Client Response

Getting Started

Use the interactive scaffolding CLI to create a configured ExisJS project:

npm create exisjs@latest my-backendcd my-backendnpm run dev

The CLI configures TypeScript, builds the initial directory structure, and starts the development server at http://localhost:4000.

Option 2: Add to an Existing Project

Install the framework into an existing Node.js project:

npm install exisjsnpm install -D @exisjs/cli typescript

exisjs is the runtime your server needs in production; @exisjs/cli is the exisjs command (dev server, build, tests), a development tool.

Create an application configuration file:

exis.config.ts
import type { ExisConfig } from 'exisjs/config'
const config: ExisConfig = {  port: Number(process.env.PORT) || 4000,  cors: {    origin: '*',    methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],  },}
export default config

Add your first endpoint in src/http/health/route.ts:

src/http/health/route.ts
import { controller, route } from 'exisjs/router'
export default controller({  check: route.get('/', {    summary: 'Health check endpoint',    handle: () => ({ status: 'ok', uptime: process.uptime() }),  }),})

Next Steps

Explore the documentation to learn more about developing with ExisJS: