@glandjs/hono@1.1.0-beta
@glandjs/hono
The Hono 4 transport — one HTTP layer that also runs on Cloudflare Workers, Deno, Bun and anywhere else the Fetch API runs.
@glandjs/hono binds your controllers and channels to a Hono 4
application. It is the transport that also runs at the edge: the same module that
listen()s on Node exports as a plain Fetch handler everywhere else.
pnpm add @glandjs/core @glandjs/common @glandjs/events @glandjs/http @glandjs/hono hono reflect-metadataAttaching it
Section titled “Attaching it”import { GlandFactory } from '@glandjs/core'import { Controller, Module } from '@glandjs/common'import { Get } from '@glandjs/http'import { HonoBroker, type HonoRequestContext } from '@glandjs/hono'
@Controller('/products')export class ProductController { @Get('/:id') async find(ctx: HonoRequestContext) { return { product: await ctx.call('db:product:find', { id: ctx.params.id }) } }}
@Module({ controllers: [ProductController] })export class AppModule {}
const { app } = await GlandFactory.create(AppModule)const http = app.connectTo(HonoBroker)
http.listen(3000)await http.ready()Exports
Section titled “Exports”| Symbol | Description |
|---|---|
HonoBroker<TEvents> |
The broker you pass to connectTo(). |
HonoRequestContext<TEvents> |
The context your handlers receive. |
HonoCore<TEvents> |
The application object, plus http.hono. |
HonoAdapter<TEvents> |
The transport implementation. |
The edge
Section titled “The edge”// Cloudflare Workers, Deno Deploy, Bun, Lambdaexport default http.honolisten() is Node-only. On any other runtime it logs that it is running on a non-Node
runtime and returns without an error, so the same module serves both worlds.
| Runtime | How it listens |
|---|---|
| Node | @hono/node-server is loaded on demand |
| Cloudflare Workers / Deno / Bun | export http.hono; listen() is a no-op |
The Fetch consequences
Section titled “The Fetch consequences”A Fetch Response is immutable. That single fact explains most of what follows.
- No
rawbody. A fetch body is read as text. The adapter stores it inctx.rawTextand parsesctx.bodyfrom theContent-Type. Declaringraw()logs a warning at boot rather than failing later. useRaw()can add headers only. A framework middleware may add headers, which the adapter merges with the response taking precedence; it cannot amend the status or the body, but it can short-circuit by returning its ownResponse.ctx.ipisundefinedlocally. It is derived fromX-Forwarded-ForandX-Real-IP, because a Fetch request has no socket.signedCookiesis{}.
Multipart
Section titled “Multipart”Hono handles it through the platform, not through a parser:
@Get('/upload')async upload(ctx: HonoRequestContext) { const form = await ctx.req.parseBody() return { names: Object.keys(form) }}Declaring multipart() logs a warning telling you to call parseBody() yourself.
What it cannot do
Section titled “What it cannot do”rawbodies — see above.- Multipart through
bodyParser()— usec.req.parseBody(). https/trustProxyconfiguration: the runtime owns the socket, so the adapter only handlespoweredBy.- Signed cookies.
Everything else — the onion, extended verbs, CORS, cookies, SSE, close() — behaves
exactly as the matrix says.