ExisJS

Middleware

ExisJS comes with a collection of built-in middleware out of the box.

ExisJS provides first-party implementations for features like CORS, Request Logging, Backpressure, In-Memory Rate Limiting, Idempotency, and Deduplication.

All middleware can be imported directly from the exisjs/middleware package.


Where to Apply Middleware

ExisJS gives you granular control over where your middleware executes:

  1. Globally (server.ts): Applies to every request hitting your server via app.use().
  2. Folder-Scoped (boundary.ts): Applies to routes inside that folder and all subfolders automatically.
  3. Route-Scoped (route.ts): Applies only to a specific endpoint.
src/http/admin/boundary.ts
import { defineBoundary } from 'exisjs/router'import type { Request, Response, Next } from 'exisjs/router'import { requestLogger } from 'exisjs/middleware'
export const config = defineBoundary({})
// Auto-detected chain step for /admin/*export const logger = requestLogger({ level: 'debug' })

Middleware vs Inline Execution

Middleware executes before your route handler. If you are using custom typed contexts to destructure variables (e.g., async handle({ user })), you must use middleware to populate req.user.

Functional Paradigm

src/http/route.ts
import { controller, route } from 'exisjs/router'import { validate } from 'exisjs/middleware'import { tex } from 'exisjs/validator'
const mySchema = {  body: tex.object({ name: tex.string() })}
export default controller({  create: route.post('/', {    middlewares: [validate(mySchema)],    handle({ req }) {      return { ok: true, name: req.body.name }    }  })})

Class-Based (OOP) Paradigm

src/http/route.ts
import { Controller, Post, Use } from 'exisjs/decorators'import { validate } from 'exisjs/middleware'import { tex } from 'exisjs/validator'
const mySchema = {  body: tex.object({ name: tex.string() })}
@Controller()export default class MyController {  @Use(validate(mySchema))  @Post('/')  async create() {    return { ok: true }  }}

1. CORS

The cors middleware handles Cross-Origin Resource Sharing.

The recommended practice in ExisJS is to configure CORS globally in your exis.config.ts file or per-folder in boundary.ts.

exis.config.ts
import { defineConfig } from 'exisjs/config'
export default async () => defineConfig({  cors: {    origin: ['https://myapp.com', /localhost:\d+/],    methods: ['GET', 'POST', 'OPTIONS'],    credentials: true,    maxAge: 86400,  }})

2. Request Logger

ExisJS uses Pino under the hood. The requestLogger injects a request-scoped logger instance (req.log) and times response cycles automatically.

src/http/server.ts
import { requestLogger } from 'exisjs/middleware'
app.use(requestLogger({  level: 'debug',  pretty: true}))

3. Request ID

Injects a unique X-Request-Id into every incoming request and outgoing response header. Supports distributed tracing headers (traceparent, x-b3-traceid).

src/http/server.ts
import { requestId } from 'exisjs/middleware'
app.use(requestId())

4. Rate Limiting (In-Memory / Native Off-Heap)

ExisJS provides high-performance rate limiting powered by Rust native memory:

src/http/boundary.ts
import { rateLimit } from 'exisjs/middleware'
export const limiter = rateLimit({  windowMs: 60000,  max: 100,})
DEFAULT

[!NOTE] Core rate limiting runs completely off-heap in-memory. For Redis-backed distributed rate limiting across clusters, use @exisjs/cache.


5. Idempotency (In-Memory / Native Off-Heap)

Idempotency caches responses based on the Idempotency-Key header, preventing duplicate mutations:

src/http/checkout/route.ts
import { Controller, Post, Idempotent } from 'exisjs/decorators'
@Controller('/checkout')export default class CheckoutController {  @Idempotent({ ttlMs: 86400000 })  @Post('/')  async checkout() {    return { success: true }  }}

6. Request Deduplication (dedupe)

If multiple clients request the exact same resource concurrently, dedupe collapses identical in-flight requests into a single operation:

src/http/stats/route.ts
import { controller, route } from 'exisjs/router'import { dedupe } from 'exisjs/middleware'
export default controller({  getStats: route.get('/', {    middlewares: [dedupe({ ttlMs: 5000 })],    async handle() {      return { activeUsers: 1000 }    }  })})

7. Static File Serving

A static file server that resolves MIME types and streams files:

import { serveStatic } from 'exisjs/middleware'import path from 'node:path'
app.use('/public', serveStatic(path.join(process.cwd(), 'public'), {  maxAge: 31536000}))

8. Compression (Gzip / Brotli / Deflate)

Stream-based compression middleware supporting Gzip, Deflate, and Brotli:

exis.config.ts
import type { ExisConfig } from 'exisjs/config'
const config: ExisConfig = {  compression: true,}
export default config