Runtime
The dependency-injection container, module bootstrap, lifecycle hooks and the Context base class.
Learn Gland in three passes — what it is, how to build an application, and how the packages work.
npm install @glandjs/core @glandjs/common @glandjs/events @glandjs/http @glandjs/nodeimport { Controller } from '@glandjs/common'import { Get } from '@glandjs/http'import type { ExpressContext } from '@glandjs/express'import type { EventTypes } from '../shared/events.interface'
@Controller('products')export class ProductController { @Get() async list(ctx: ExpressContext<EventTypes>) { const products = await ctx.call('db:product:all', {}) return ctx.send({ products }) }}Controllers reply. They never touch the database.
import { Channel, On } from '@glandjs/common'import type { Product } from '../shared/events.interface'
@Channel('db')export class Database { private products = new Map<string, Product>()
@On('product:all') async allProducts(): Promise<Product[]> { return Array.from(this.products.values()) }}Channels own side effects, addressed as db:product:all.
import 'reflect-metadata'import { GlandFactory } from '@glandjs/core'import { ExpressBroker } from '@glandjs/express'import { AppModule } from './app.module'
async function bootstrap() { const { app, shutdown } = await GlandFactory.create(AppModule) const http = app.connectTo(ExpressBroker) http.listen(3000) await http.ready()}void bootstrap()Brokers carry the transport. Swap the adapter, keep the app.
Gland separates what your application does from how it is transported. A controller receives an interaction and emits a domain event. A channel subscribes to that event and performs the side effect. A broker connects the two to a real protocol.
That single idea produces a codebase where business logic can be tested without an HTTP server, where the same domain code runs over more than one transport, and where adding a new endpoint never means rewriting a service.
Controllers never call business logic directly. They emit a domain event, and channels decide what happens. Side effects become observable, replaceable and testable.
The HTTP layer contains no server, no router and no framework import. Express, Fastify, Koa, Hono and node:http each implement the same contract, and the same controller runs unchanged on all five.
The emitter is three methods and a couple of kilobytes. The broker adds channels, meshes and watchers on top of it. Nothing is hidden from you, and there is nothing to unlearn.
Event names and payload shapes live in one typed map. A renamed event or a changed payload is a compile error, not a production incident.
A controller test asserts the events it emitted. A channel test calls a handler with a fake context. Because side effects are concentrated in channels, test suites stay small.
Adding a transport means implementing one abstract adapter and its context. Because a broker is just a class, WebSocket or RPC is the same exercise — and there is no big-bang migration.
Gland is a set of small, independently installable packages. Adopt only what you need.
Runtime
The dependency-injection container, module bootstrap, lifecycle hooks and the Context base class.
Decorators
The decorators and metadata keys that make up the DSL: Module, Controller, Channel, On and Inject.
Eventing
The namespaced event broker: channels, mesh networking, request/response calls and async watchers.
Primitive
A zero-dependency, tree-routed event emitter with wildcard matching. Three methods, nothing else.
Protocol
The framework-agnostic HTTP core: route decorators, HttpContext, the middleware onion, HttpCore and the HttpBroker.
Adapter
The Express 5 adapter. Wires Gland controllers and channels to a real HTTP server.
Adapter
The Fastify 5 adapter, with the middleware onion composed into a single real hook.
Adapter
The Koa 3 adapter: a native router, native extended verbs and a real promise onion.
Adapter
The Hono 4 adapter, running on Node and on the edge through the Fetch API.
Adapter
The node:http adapter: the reference implementation of the contract, with no framework and no dependencies.
The long documentation is not written here. Every API reference, the HTTP guides, the adapter matrix and the changelogs are published from the Gland repositories themselves on every build, so they cannot disagree with the code.
Versions below are read from each repository’s package.json when this page was built,
not typed by hand.
@glandjs/emitter 1.1.5
Tree-based storage, a hot-path LRU cache, and a CommonJS build for broader tooling support. Read the changelog
@glandjs/events 2.0.0
send() carries a payload, a channel’s watch() settles, and the call strategies
are 'all' or nothing.
Read the changelog
@glandjs/http 2.0.0
One framework-agnostic core and a pluggable adapter per server, so the same
controller runs on Express, Fastify, Koa, Hono and node:http.
Read the changelog
@glandjs/core 2.0.0
The DI container, module bootstrap, lifecycle hooks and the Context base class.
Read the changelog