npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glandjs/http

v2.0.0

Published

The framework-agnostic HTTP layer for Gland: adapter contracts, a promise-based middleware onion, and a uniform request context.

Readme

Description

What if HTTP was just another event?

@glandjs/http contains no server, no router and no framework import. It is three things:

  • the contracts — what a handler may return, what a middleware is, what a route is
  • the machinery — the request pipeline, the middleware onion, the lifecycle bus, the error renderer, the CORS policy
  • the decorators — so a controller can declare a route without knowing how it will be served

Everything that has to be identical across transports lives here. Everything that needs framework knowledge lives in an adapter.

| Package | Framework | Middleware onion | Runtime | | -------------------------------- | ----------- | ---------------- | ----------------------- | | @glandjs/express | Express 5 | hand-off | Node | | @glandjs/fastify | Fastify 5 | real | Node | | @glandjs/koa | Koa 3 | real | Node | | @glandjs/hono | Hono 4 | real | Node and the edge | | @glandjs/node | node:http | real | Node, zero dependencies |

Install

npm install @glandjs/http

You also need a transport, @glandjs/core, @glandjs/common and reflect-metadata.

Usage

@glandjs/http is used through an adapter; there is nothing to construct here except the decorators and the middleware.

import { Get, Post, HttpReply, HttpEvent, withErrors, bodyLimitMiddleware } from '@glandjs/http';

@Controller('/orders')
class OrderController {
  @Get('/:id')
  async find(ctx) {
    const order = await ctx.call('db:order:find', ctx.params.id);
    if (!order) return ctx.throw(404, { detail: 'No such order' });
    return order; // an object becomes JSON
  }

  @Post('/')
  async create(ctx) {
    return HttpReply.json(await ctx.call('db:order:create', ctx.body), { status: 201 });
  }
}

What a handler may return

One function decides, and every adapter writes the result the same way.

| Returned | Written as | | --------------------------------- | --------------------------------------------------- | | an object, array, number, boolean | application/json | | a string | text/plain, or text/html if it sniffs as markup | | a Buffer | raw bytes | | a Readable | piped to the socket | | an SseStream | text/event-stream, with a heartbeat | | a HttpReply | exactly what it says | | undefined / null | nothing — the handler already answered |

That is why return 42 means 42 on Express and on Hono rather than a JSON 42 on one and a bare 42 on the other.

What this package owns

| | | | --------------------------------------------------------------------- | --------------------------------------------------------------- | | HttpCore | the application: routes, middleware, parsers, listen, close | | HttpBroker | what app.connectTo() takes; registers the binder's routes | | HttpServerAdapter | the contract an adapter implements | | HttpContext | the whole request surface, abstract, and identical everywhere | | HttpReply | an explicit reply | | createCorsMiddleware | one CORS policy for every adapter | | errorHandler, withErrors, bodyLimitMiddleware | the error and body primitives | | SseStream | a correctly framed event stream | | toReplyPayload, normalizePath, applyPrefix, parseCookieHeader | the shared decisions | | Get, Post, Propfind, … | the route decorators |

Errors

RFC 7807 problem details, and the default renderer drops the message of anything that is not an HttpException — a stack trace, a SQL fragment or a file path in a response body is a disclosure bug. Log it on http:request:error; do not send it.

ctx.throw(404, { detail: 'No such product', type: 'https://errors.example.com/product' });

Lifecycle

app.on(HttpEvent.RequestStart, ({ method, path }) => metrics.count(`${method} ${path}`));
app.on(HttpEvent.RequestEnd, ({ method, path, status, duration }) => metrics.timing('http.request', duration, { method, path, status }));

RequestEnd fires on the failure path too, so a counter cannot leak on exactly the requests that failed.

Ordering

Routes and middleware are both buffered and mounted together at listen(), in a fixed order — framework configuration, the middleware queue in call order, plugins, the routes, and finally the catch-all 404. That is what makes this work regardless of the order you wrote things in:

const http = app.connectTo(ExpressBroker); // ← routes arrive here
http.use(auth); // ← middleware arrives here
http.listen(3000); // ← everything mounts now

Documentation

License

MIT