`, AJAX headers, options) is in `CSRF-protection.md` in this folder; source: `jcc-express-mvc/lib/Security/CsrfMiddleware.ts`.\n- `auth`, `guest`, `apiAuth` — from `jcc-express-mvc` (session/JWT-style behavior depends on your auth setup).\n- `loginRateLimit` / `registerRateLimit` — aliased in the sample kernel as `loginThrottle` / `registerThrottle`.\n- `inertia(...)` — root view and SSR options for Inertia responses.\n\nTune global behavior in `kernel.middlewares`; tune per-route access with aliases on specific routes or groups.\n\n---\n\n## Inspecting middleware on routes\n\nList registered routes and their middleware:\n\n```bash\nbun artisanNode route:list\n```\n\nMiddleware runs before your controller or closure. Keep handlers small, call `next()` when appropriate, and register aliases for anything you reuse across many routes.\n","uri_path":"the-basics/middleware","versionSlug":"2-x","versionName":"2.x","navCategories":[{"navKey":"getting-started","title":"Getting Started","slug":"getting-started","items":[{"title":"Introduction","slug":"getting-started/introduction"},{"title":"Installation","slug":"getting-started/installation"},{"title":"Configuration","slug":"getting-started/configuration"},{"title":"Directory structure","slug":"getting-started/directory-structure"},{"title":"Frontend","slug":"getting-started/frontend"},{"title":"Deployment","slug":"getting-started/deployment"}]},{"navKey":"architecture-concepts","title":"Architecture Concepts","slug":"architecture-concepts","items":[{"title":"Request Lifecycle","slug":"architecture-concepts/request-lifecycle"},{"title":"Service Container","slug":"architecture-concepts/service-container"},{"title":"Service Provider","slug":"architecture-concepts/service-provider"}]},{"navKey":"the-basics","title":"The Basics","slug":"the-basics","items":[{"title":"Routing","slug":"the-basics/routing"},{"title":"Middleware","slug":"the-basics/middleware"},{"title":"CSRF protection","slug":"the-basics/csrf-protection"},{"title":"Controllers","slug":"the-basics/controllers"},{"title":"Request","slug":"the-basics/request"},{"title":"Response","slug":"the-basics/response"},{"title":"Views","slug":"the-basics/views"},{"title":"JCC Blade","slug":"the-basics/jcc-blade"},{"title":"Asset Bundling","slug":"the-basics/asset-bundling"},{"title":"Session","slug":"the-basics/session"},{"title":"Validation","slug":"the-basics/validation"},{"title":"Error Handling","slug":"the-basics/error-handling"}]},{"navKey":"digging-deeper","title":"Digging Deeper","slug":"digging-deeper","items":[{"title":"ArtisanNode","slug":"digging-deeper/artisan-node"},{"title":"Broadcasting","slug":"digging-deeper/broadcasting"},{"title":"Cache","slug":"digging-deeper/cache"},{"title":"Events","slug":"digging-deeper/events"},{"title":"File Storage","slug":"digging-deeper/file-storage"},{"title":"Helpers","slug":"digging-deeper/helpers"},{"title":"HTTP Client","slug":"digging-deeper/http-client"},{"title":"Mail","slug":"digging-deeper/mail"},{"title":"Queues","slug":"digging-deeper/queues"},{"title":"Rate Limiting","slug":"digging-deeper/rate-limiting"},{"title":"Strings","slug":"digging-deeper/strings"}]},{"navKey":"security","title":"Security","slug":"security","items":[{"title":"Authentication","slug":"security/authentication"},{"title":"Authorization","slug":"security/authorization"},{"title":"Email Verification","slug":"security/email-verification"},{"title":"Encryption","slug":"security/encryption"},{"title":"Hashing","slug":"security/hashing"}]},{"navKey":"database","title":"Database","slug":"database","items":[{"title":"Introduction","slug":"database/introduction"},{"title":"Query Builder","slug":"database/query-builder"},{"title":"Migrations","slug":"database/migrations"},{"title":"Seeding","slug":"database/seeding"},{"title":"Transactions","slug":"database/transactions"},{"title":"Mongoose","slug":"database/mongoose"},{"title":"Sequelize","slug":"database/sequelize"}]},{"navKey":"jcc-eloquent-orm","title":"JCC Eloquent ORM","slug":"jcc-eloquent-orm","items":[{"title":"Introduction","slug":"jcc-eloquent-orm/introduction"},{"title":"Defining Models","slug":"jcc-eloquent-orm/defining-models"},{"title":"Retrieving Models","slug":"jcc-eloquent-orm/retrieving-models"},{"title":"Relationships","slug":"jcc-eloquent-orm/relationships"},{"title":"Pagination","slug":"jcc-eloquent-orm/pagination"},{"title":"Mutators","slug":"jcc-eloquent-orm/mutators"},{"title":"Scopes","slug":"jcc-eloquent-orm/scopes"},{"title":"Soft Deleting","slug":"jcc-eloquent-orm/soft-deleting"},{"title":"Events & Observers","slug":"jcc-eloquent-orm/events-and-observers"}]},{"navKey":"testing","title":"Testing","slug":"testing","items":[{"title":"Introduction","slug":"testing/introduction"},{"title":"Testing Overview","slug":"testing/testing-overview"},{"title":"Unit Testing","slug":"testing/unit-testing"},{"title":"Feature Testing","slug":"testing/feature-testing"},{"title":"Database Testing","slug":"testing/database-testing"}]},{"navKey":"packages","title":"Packages","slug":"packages","items":[{"title":"JWT","slug":"packages/jwt"},{"title":"Socialite","slug":"packages/socialite"}]}],"prevPage":{"title":"Routing","uri_path":"the-basics/routing"},"nextPage":{"title":"CSRF protection","uri_path":"the-basics/csrf-protection"},"searchPages":[{"title":"Introduction","uri_path":"getting-started/introduction","excerpt":"Getting Started — Introduction"},{"title":"Installation","uri_path":"getting-started/installation","excerpt":"Getting Started — Installation"},{"title":"Configuration","uri_path":"getting-started/configuration","excerpt":"Getting Started — Configuration"},{"title":"Directory structure","uri_path":"getting-started/directory-structure","excerpt":"Getting Started — Directory structure"},{"title":"Frontend","uri_path":"getting-started/frontend","excerpt":"Getting Started — Frontend"},{"title":"Deployment","uri_path":"getting-started/deployment","excerpt":"Getting Started — Deployment"},{"title":"Request Lifecycle","uri_path":"architecture-concepts/request-lifecycle","excerpt":"Architecture Concepts — Request Lifecycle"},{"title":"Service Container","uri_path":"architecture-concepts/service-container","excerpt":"Architecture Concepts — Service Container"},{"title":"Service Provider","uri_path":"architecture-concepts/service-provider","excerpt":"Architecture Concepts — Service Provider"},{"title":"Routing","uri_path":"the-basics/routing","excerpt":"The Basics — Routing"},{"title":"Middleware","uri_path":"the-basics/middleware","excerpt":"The Basics — Middleware"},{"title":"CSRF protection","uri_path":"the-basics/csrf-protection","excerpt":"The Basics — CSRF protection"},{"title":"Controllers","uri_path":"the-basics/controllers","excerpt":"The Basics — Controllers"},{"title":"Request","uri_path":"the-basics/request","excerpt":"The Basics — Request"},{"title":"Response","uri_path":"the-basics/response","excerpt":"The Basics — Response"},{"title":"Views","uri_path":"the-basics/views","excerpt":"The Basics — Views"},{"title":"JCC Blade","uri_path":"the-basics/jcc-blade","excerpt":"The Basics — JCC Blade"},{"title":"Asset Bundling","uri_path":"the-basics/asset-bundling","excerpt":"The Basics — Asset Bundling"},{"title":"Session","uri_path":"the-basics/session","excerpt":"The Basics — Session"},{"title":"Validation","uri_path":"the-basics/validation","excerpt":"The Basics — Validation"},{"title":"Error Handling","uri_path":"the-basics/error-handling","excerpt":"The Basics — Error Handling"},{"title":"ArtisanNode","uri_path":"digging-deeper/artisan-node","excerpt":"Digging Deeper — ArtisanNode"},{"title":"Broadcasting","uri_path":"digging-deeper/broadcasting","excerpt":"Digging Deeper — Broadcasting"},{"title":"Cache","uri_path":"digging-deeper/cache","excerpt":"Digging Deeper — Cache"},{"title":"Events","uri_path":"digging-deeper/events","excerpt":"Digging Deeper — Events"},{"title":"File Storage","uri_path":"digging-deeper/file-storage","excerpt":"Digging Deeper — File Storage"},{"title":"Helpers","uri_path":"digging-deeper/helpers","excerpt":"Digging Deeper — Helpers"},{"title":"HTTP Client","uri_path":"digging-deeper/http-client","excerpt":"Digging Deeper — HTTP Client"},{"title":"Mail","uri_path":"digging-deeper/mail","excerpt":"Digging Deeper — Mail"},{"title":"Queues","uri_path":"digging-deeper/queues","excerpt":"Digging Deeper — Queues"},{"title":"Rate Limiting","uri_path":"digging-deeper/rate-limiting","excerpt":"Digging Deeper — Rate Limiting"},{"title":"Strings","uri_path":"digging-deeper/strings","excerpt":"Digging Deeper — Strings"},{"title":"Authentication","uri_path":"security/authentication","excerpt":"Security — Authentication"},{"title":"Authorization","uri_path":"security/authorization","excerpt":"Security — Authorization"},{"title":"Email Verification","uri_path":"security/email-verification","excerpt":"Security — Email Verification"},{"title":"Encryption","uri_path":"security/encryption","excerpt":"Security — Encryption"},{"title":"Hashing","uri_path":"security/hashing","excerpt":"Security — Hashing"},{"title":"Introduction","uri_path":"database/introduction","excerpt":"Database — Introduction"},{"title":"Query Builder","uri_path":"database/query-builder","excerpt":"Database — Query Builder"},{"title":"Migrations","uri_path":"database/migrations","excerpt":"Database — Migrations"},{"title":"Seeding","uri_path":"database/seeding","excerpt":"Database — Seeding"},{"title":"Transactions","uri_path":"database/transactions","excerpt":"Database — Transactions"},{"title":"Mongoose","uri_path":"database/mongoose","excerpt":"Database — Mongoose"},{"title":"Sequelize","uri_path":"database/sequelize","excerpt":"Database — Sequelize"},{"title":"Introduction","uri_path":"jcc-eloquent-orm/introduction","excerpt":"JCC Eloquent ORM — Introduction"},{"title":"Defining Models","uri_path":"jcc-eloquent-orm/defining-models","excerpt":"JCC Eloquent ORM — Defining Models"},{"title":"Retrieving Models","uri_path":"jcc-eloquent-orm/retrieving-models","excerpt":"JCC Eloquent ORM — Retrieving Models"},{"title":"Relationships","uri_path":"jcc-eloquent-orm/relationships","excerpt":"JCC Eloquent ORM — Relationships"},{"title":"Pagination","uri_path":"jcc-eloquent-orm/pagination","excerpt":"JCC Eloquent ORM — Pagination"},{"title":"Mutators","uri_path":"jcc-eloquent-orm/mutators","excerpt":"JCC Eloquent ORM — Mutators"},{"title":"Scopes","uri_path":"jcc-eloquent-orm/scopes","excerpt":"JCC Eloquent ORM — Scopes"},{"title":"Soft Deleting","uri_path":"jcc-eloquent-orm/soft-deleting","excerpt":"JCC Eloquent ORM — Soft Deleting"},{"title":"Events & Observers","uri_path":"jcc-eloquent-orm/events-and-observers","excerpt":"JCC Eloquent ORM — Events & Observers"},{"title":"Introduction","uri_path":"testing/introduction","excerpt":"Testing — Introduction"},{"title":"Testing Overview","uri_path":"testing/testing-overview","excerpt":"Testing — Testing Overview"},{"title":"Unit Testing","uri_path":"testing/unit-testing","excerpt":"Testing — Unit Testing"},{"title":"Feature Testing","uri_path":"testing/feature-testing","excerpt":"Testing — Feature Testing"},{"title":"Database Testing","uri_path":"testing/database-testing","excerpt":"Testing — Database Testing"},{"title":"JWT","uri_path":"packages/jwt","excerpt":"Packages — JWT"},{"title":"Socialite","uri_path":"packages/socialite","excerpt":"Packages — Socialite"}]},"url":"/docs/2-x/the-basics/middleware","version":"1"}">
JCC Express

Middleware

Introduction

Middleware sits in the HTTP pipeline between the incoming request and your route handler. Each piece is a function with the Express shape (req, res, next) => void (or async). It can run logic, short-circuit with a response, or call next() to continue.

JCC Express MVC distinguishes two layers:

  • Global middleware — wired once on the Express app for every request (framework defaults plus your HTTP kernel’s middlewares array).
  • Route middleware — attached only to routes that reference it, using string aliases from the kernel or raw Express handlers.

For how a request walks the full stack end-to-end, see Request lifecycle (final-documentation/Artiecture Concept/Request-Lifecycle.md).


HTTP kernel

Your application’s HTTP kernel lives at app/Http/kernel.ts. It is registered on the container as HttpKernel (via bootstrap/app.ts.withKernel(Kernel)).

The class must satisfy KernelInterface:

  • middlewaresRequestHandler[] appended to the global Express stack after the framework’s built-in setup (body parsers, session, CORS, logging, rate limiting, HttpRequest / HttpResponse, and the per-request container binding for request / response / next).
  • middlewareAliasesRecord<string, RequestHandler>: short names resolved when you pass strings to Route.middleware([...]).

Example shape:

TypeScript

Order in middlewares is execution order. Put cross-cutting concerns here (CSRF, method spoofing, Inertia) when they should apply broadly; use aliases for guards and throttles you attach selectively on routes.


Framework global stack (before your kernel)

The Middleware class inside jcc-express-mvc always registers a fixed prefix of Express middleware before spreading kernel.middlewares. That includes JSON/urlencoded parsing, cookies, session, flash, file uploads, CORS, Morgan, the app’s rate limit config, and the wrapper that attaches HttpRequest / HttpResponse / jccSession. For req.session, req.jccSession, flash, and session(), see Session.md.

After those run, another middleware binds request, response, and next on the container for each request (used by request(), response(), view(), inertia(), etc.).

The numbered list of default middleware is documented in Request lifecycle; you normally do not edit that class from an app—customize CORS and rate limiting through app/Config where applicable, and extend behavior via the kernel or route middleware.


Route middleware

Attaching middleware

Use Route.middleware(...) immediately before the verb you register. The argument may be:

  • A string — must exist on kernel.middlewareAliases.
  • An array — mix strings (resolved to aliases) and raw RequestHandler functions.
  • A single RequestHandler — used as-is.
TypeScript

If an alias string is missing, RouteBuilder throws (for example auth middleware not found in alias middleware).

Groups

Inside Route.prefix(...).group(...) or Route.group(...), call Route.middleware([...]) before defining routes. Nested groups flatten parent group middleware first, then child group middleware, then any middleware chained right before Route.get / post / etc.

TypeScript

When the route is registered on Express, global stack → matched route → route middleware (in that order) → handler.


Writing custom middleware

A middleware is a RequestHandler: (req, res, next) => void. Call next() when the request should continue; pass next(err) to delegate to the error handler. For async work, either use async / await and catch errors, or wrap with the framework’s asyncHandler if you need consistent error propagation.

Register reusable app middleware as functions or small modules, then either:

  • Add them to kernel.middlewareAliases under a name and reference that name in routes, or
  • Pass the function directly into Route.middleware(myHandler) or an array alongside strings.

Example alias:

TypeScript
TypeScript
TypeScript

Built-in and framework exports

Common pieces you import rather than reimplement:

  • csrf() / methodSpoofing() from jcc-express-mvc/Core — web forms and unsafe verbs. Full CSRF usage (, AJAX headers, options) is in CSRF-protection.md in this folder; source: jcc-express-mvc/lib/Security/CsrfMiddleware.ts.
  • auth, guest, apiAuth — from jcc-express-mvc (session/JWT-style behavior depends on your auth setup).
  • loginRateLimit / registerRateLimit — aliased in the sample kernel as loginThrottle / registerThrottle.
  • inertia(...) — root view and SSR options for Inertia responses.

Tune global behavior in kernel.middlewares; tune per-route access with aliases on specific routes or groups.


Inspecting middleware on routes

List registered routes and their middleware:

Bash

Middleware runs before your controller or closure. Keep handlers small, call next() when appropriate, and register aliases for anything you reuse across many routes.