@glandjs/express@1.1.0-beta
@glandjs/express
The Express 5 transport.
# pnpm pnpm add @glandjs/express# npm npm install @glandjs/express# yarn yarn add @glandjs/express# bun bun add @glandjs/express| Package | @glandjs/express |
| Version | 1.1.0-beta |
| Repository | glandjs/http |
| License | MIT |
| Node | >= 20 |
| Dependencies | @glandjs/core ^1.0.3-beta · @glandjs/events ^1.1.0 · @medishn/toolkit ^1.0.4 · express ^5.1.0 · tslib ^2.8.1 |
| Peer dependencies | @glandjs/common ^1.0.3-beta · reflect-metadata ^0.2.2 |
Description
Section titled “Description”Express is not your application — it is just one way to deliver HTTP. Gland abstracts that detail.
@glandjs/express is the Express adapter for Gland’s HTTP layer. It contributes
an Express application, an ExpressContext, and nothing else — the onion, the
request lifecycle, the reply coercion and the error rendering all come from
@glandjs/http, so the same controller runs unchanged on Fastify, Koa, Hono or
node:http.
A controller never imports Express. A route is declared with a decorator from
@glandjs/http, and the adapter works out how to write the reply to the socket.
npm install @glandjs/core @glandjs/common @glandjs/http @glandjs/express reflect-metadataimport { GlandFactory } from '@glandjs/core';import { ExpressBroker, type ExpressContext } from '@glandjs/express';import { Get } from '@glandjs/http';import { Controller, Module } from '@glandjs/common';
@Controller('/products')class ProductController { @Get('/:id') async find(ctx: ExpressContext) { return ctx.call('db:product:find', ctx.params.id); // an object becomes JSON }}
@Module({ controllers: [ProductController] })class AppModule {}
const { app, shutdown } = await GlandFactory.create(AppModule);const http = app.connectTo(ExpressBroker, { poweredBy: false });
http.json();http.use(async (ctx, next) => { const started = Date.now(); await next(); console.log(`${ctx.method} ${ctx.path} ${Date.now() - started}ms`);});http.useRaw(compression()); // framework middleware, unmolested
http.listen(3000);What is different about Express
Section titled “What is different about Express”await next() resolves early. Express’s next() is a hand-off, not a call.
The bridge resolves as soon as Express moves downstream, so an upstream
try/catch cannot see a downstream throw. The error still reaches the error
handler — next(err) is called for you — but you cannot measure or transform
what happened after the hand-off.
app.use(async (ctx, next) => { const started = Date.now(); await next(); // the hand-off, not the route metrics.timing('route', Date.now() - started);});The transport-agnostic alternative, which works everywhere:
app.on(HttpEvent.RequestEnd, ({ method, path, status, duration }) => { metrics.timing('http.request', duration, { method, path, status });});app.use is split in two. Gland’s use() is a promise-based onion;
Express’s is a synchronous hand-off. Rather than infer intent from a function’s
arity — which the previous version did, and which silently mis-typed every
Express handler — the two are separate:
app.use((ctx, next) => …); // Gland, on the onionapp.useRaw(compression()); // Express, unmolestedctx.send('done') is text/plain. Express’s res.send(string) defaults to
text/html, which would make a text reply render as a page in a browser. The
adapter sets the type, so a string means the same thing on all five adapters.
Extended methods use a guard. Express 5 exposes seven verbs, so @Propfind()
is registered with all() plus a method check — a real route that works, rather
than a TypeError: router.propfind is not a function at boot.
Reaching Express directly
Section titled “Reaching Express directly”app.instance.set('trust proxy', 1); // a setting Gland does not modelapp.set('etag', 'strong'); // the same thing, typedThat is the right answer for framework settings. It is the wrong answer for routing, body parsing or CORS, because those are the parts the abstraction owns.
| Export | Kind |
|---|---|
ExpressBroker / ExpressBrokerClass |
What app.connectTo() takes |
ExpressCore |
The application, with instance typed and set() |
ExpressAdapter |
The adapter |
ExpressContext |
The context |
EXPRESS_VERBS, ExpressApp |
Extras |
The full surface is in the API reference.
Documentation
Section titled “Documentation”- Choosing an adapter
- Middleware — and the Express caveat
- Adapter matrix — what each transport can and cannot do
- Gland architecture
License
Section titled “License”MIT