Middleware
Morphis uses middleware as its real execution model. Global middleware wraps every matched request, and decorator-style middleware attaches route-specific behavior to controller methods.
This page covers the built-in middleware and decorators that are ready to use today.
How middleware fits together
In Morphis, the request flow is assembled from two layers:
- global middleware registered with
router.use(...) - route-level decorators or endpoint middleware such as
@Validate(...),@Transform(...), and@Connect(...)
That split lets you keep cross-cutting concerns global while still attaching request-specific behavior at the controller method level.
Global middleware
These are the built-in pieces intended for router.use(...).
Logger
Logger is a singleton LoggerMiddleware instance.
router.use(Logger)What it does:
- patches the global
consolemethods once - reads request-local values from
current - records the resolved request path in
current.path - helps downstream logs carry request context
Use it when you want request-aware logs without manually passing tracing values around.
Track
Track is both a global middleware instance and a decorator.
router.use(Track)What it does:
- creates
current.trackIdif it does not already exist - adds
X-Track-Idto the outgoing response - wraps plain return values in
Response.json({ data: ... }) - logs normalized error details before rethrowing
Use it when you want request correlation IDs across logs and responses.
Cors(options?)
Cors(...) creates a CorsMiddleware instance.
router.use(Cors({ origins: '*' }))What it does:
- handles
OPTIONSpreflight requests by returning204 - sets CORS headers for allowed origins
- can also decorate a final
Responsewith those headers
Supported options:
originsmethodsheadersmaxAge
Use it when your API is called from browsers or multiple frontends.
If the browser still reports that CORS is rejected, see Troubleshooting for a quick diagnosis and the router.use(Cors(...)) setup pattern.
Route decorators and endpoint middleware
These built-ins are attached to controller methods or inline route definitions.
@Controller(path)
@Controller(...) is a class decorator backed by ControllerMiddleware.
@Controller('/posts')
export class PostController {}What it does:
- stores the controller base path
- combines that base path with route method decorators
- stamps route metadata onto bound controller methods
Use it to define the base path for a controller once.
@Get(), @Post(), @Put(), @Delete(), @Patch()
These are method decorators backed by HttpMethodMiddleware.
@Get(':id')
async get(req: Request) {}What they do:
- store the HTTP verb for the route
- normalize the declared path segment
- register route metadata that the router reads later
Use them on controller methods or as inline route middleware such as router.get(handler, [Get('/health')]).
@Validate(map)
@Validate(...) is a method decorator backed by ValidateMiddleware.
@Validate({ body: PostBodyValidator, params: PostParamsValidator })What it does:
- runs configured validators in parallel
- merges validation errors across request sources
- throws a validation error when any check fails
- rewrites
req.body,req.query, andreq.paramswith validator output on success
Use it when you want correctness checks and sanitized request data before the controller runs.
See Validation for the full validator model.
@Transform(map)
@Transform(...) is a method decorator backed by TransformerMiddleware.
@Transform({ body: PostBodyTransformer, res: PostResponseTransformer })What it does:
- transforms
headers,body,params, andquerybefore the handler - optionally transforms the returned value through
res - mutates the request object before controller code sees it
Use it when you want normalization, DTO shaping, or response mapping.
See Transformer for the full transform model.
@Connect(name?) or Connect(name?)
Connect(...) is a method decorator and endpoint middleware backed by ConnectMiddleware.
@Connect('default')or
router.get(handler, [Get('/posts'), Connect('default')])What it does:
- resolves the named database connection lazily
- stores the resolved driver handle on
current.db[name] - returns HTTP 503 if the connection is unavailable
- leaves unresolved connections as
nulloncurrent.db
Use it when a handler needs a database connection inside the request lifecycle.
To understand the database side of this flow, see Database Connections and Database Models.
@Track or @Track()
Track also works as a method or class decorator.
@Track()
export class PostController {}What it does:
- wraps a single method or every method in a class
- ensures a request track id exists
- adds
X-Track-Idto the response
Use it when you want request tracking only for selected controllers or handlers instead of globally.
Typical setup
import { Connect, Cors, Logger, Router, Track } from 'morphis';
const router = new Router();
router.use([
Logger,
Track,
Cors({ origins: '*' }),
Connect('default'),
]);Connect('default') can be registered globally or per handler. Use the narrowest scope that matches your application, especially if only some routes need database access.