Skip to content
Node.js · TypeScript · Event-driven

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/node
  • 3method emitter
  • 0runtime deps in the emitter
  • MITlicensed
product.controller.ts
import { 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.

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.

Event-driven by default

Controllers never call business logic directly. They emit a domain event, and channels decide what happens. Side effects become observable, replaceable and testable.

One core, five transports

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.

Tiny primitives

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.

TypeScript-first

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.

Testable by construction

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.

Extensible seam

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.

@glandjs/core2.0.0

Runtime

The dependency-injection container, module bootstrap, lifecycle hooks and the Context base class.

@glandjs/common2.0.0

Decorators

The decorators and metadata keys that make up the DSL: Module, Controller, Channel, On and Inject.

@glandjs/events2.0.0

Eventing

The namespaced event broker: channels, mesh networking, request/response calls and async watchers.

@glandjs/emitter1.1.5

Primitive

A zero-dependency, tree-routed event emitter with wildcard matching. Three methods, nothing else.

@glandjs/http2.0.0

Protocol

The framework-agnostic HTTP core: route decorators, HttpContext, the middleware onion, HttpCore and the HttpBroker.

@glandjs/koa1.0.0

Adapter

The Koa 3 adapter: a native router, native extended verbs and a real promise onion.

@glandjs/node1.0.0

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