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

@triadjs/hono

v0.2.10

Published

Hono adapter for Triad routers — Node, Deno, Bun, Cloudflare Workers

Readme

@triadjs/hono

First-party Hono adapter for Triad — mount a Triad router onto Hono and run it anywhere Hono runs: Cloudflare Workers, Deno, Bun, Node.js, Fastly, Lagon.

Use this adapter when you want Triad on an edge runtime. For a traditional Node server with WebSocket / channel support, use @triadjs/fastify. For a middleware-style integration into an existing Express app, use @triadjs/express.

Install

npm install @triadjs/core @triadjs/hono hono

Basic usage

import { createTriadApp } from '@triadjs/hono';
import router from './src/app.js';

const app = createTriadApp(router, {
  services: { petRepo, adoptionSaga },
});

export default app;

Node.js

import { serve } from '@hono/node-server';
serve({ fetch: app.fetch, port: 3000 });

Cloudflare Workers / Deno / Fastly

export default app;

Bun

export default { fetch: app.fetch };

Services injection

Pass a static object for simple apps:

createTriadApp(router, {
  services: { petRepo, adoptionSaga },
});

Or a factory called once per request. The factory receives the standard Fetch Request, so you can read headers for tenant lookups, auth scopes, and DB connections:

createTriadApp(router, {
  services: (req) => ({
    petRepo: petRepoFor(req.headers.get('x-tenant') ?? 'default'),
    user: authFromHeader(req.headers.get('authorization')),
  }),
});

Async factories are supported:

createTriadApp(router, {
  services: async (req) => {
    const tenant = await lookupTenant(req.headers.get('x-tenant'));
    return { petRepo: await petRepoFor(tenant) };
  },
});

Mounting under a prefix

The returned app is a standard Hono instance, so compose it with parent.route(prefix, triadApp):

import { Hono } from 'hono';
import { createTriadApp } from '@triadjs/hono';

const triadApp = createTriadApp(router, { services });

const app = new Hono();
app.get('/health', (c) => c.json({ ok: true }));
app.route('/api/v1', triadApp);

export default app;

Error envelope

Request validation failures return a 400 with the same envelope as @triadjs/express and @triadjs/fastify:

{
  "code": "VALIDATION_ERROR",
  "message": "Request body failed validation: name: String must be at least 1 character",
  "errors": [
    { "path": "name", "message": "String must be at least 1 character", "code": "string_too_short" }
  ]
}

If a handler returns a body that does not match its declared response schema, the adapter returns 500 with:

{
  "code": "INTERNAL_ERROR",
  "message": "The server produced an invalid response."
}

Response-validation failures are logged via the logError option (defaults to console.error), because they always indicate a server bug — never trust a handler that ships invalid data.

Malformed JSON in a POST/PUT/PATCH body also returns the validation envelope:

{ "code": "VALIDATION_ERROR", "message": "Request body failed validation: <root>: Request body is not valid JSON", "errors": [...] }

Empty responses (204, 205, 304)

For endpoints declared with 204 (or 205, 304) responses, the adapter calls c.body(null, status) to send an empty body — no Content-Type is set and res.text() yields ''. Declare the response with an optional schema:

responses: {
  204: { schema: t.unknown().optional(), description: 'Deleted' },
}

And respond with ctx.respond[204](undefined).

File uploads

Endpoints whose request body contains at least one t.file() field are automatically routed through Hono's built-in c.req.parseBody({ all: true }) and normalized into TriadFile instances before handing the body to your handler. No extra dependencies are needed — file uploads work out of the box on every Hono-supported runtime (Node, Bun, Deno, Cloudflare Workers).

import { t, endpoint, type TriadFile } from '@triadjs/core';

const AvatarUpload = t.model('AvatarUpload', {
  name: t.string(),
  avatar: t.file().maxSize(5_000_000).mimeTypes('image/png', 'image/jpeg'),
});

export const uploadAvatar = endpoint({
  name: 'uploadAvatar',
  method: 'POST',
  path: '/avatars',
  summary: 'Upload an avatar',
  request: { body: AvatarUpload },
  responses: {
    201: { schema: t.model('Ok', { url: t.string() }), description: 'Uploaded' },
  },
  handler: async (ctx) => {
    const file: TriadFile = ctx.body.avatar;
    return ctx.respond[201]({ url: `/u/${file.name}` });
  },
});

Schema-level maxSize / mimeTypes / minSize violations produce the standard VALIDATION_ERROR envelope, byte-for-byte identical across the Fastify, Express, and Hono adapters.

Runtime notes

  • Cloudflare Workers: console.error works out of the box. process.env does not — pass services via env by constructing them inside a per-request factory that closes over the worker's env binding.
  • Bun: native support, no shims needed.
  • Deno: ESM imports work as-is.
  • Node.js: use @hono/node-server for the HTTP server. The adapter itself has no Node dependencies.

WebSocket / channels

Not supported in v1. Hono has runtime-specific websocket helpers (hono/bun, hono/cloudflare-workers, etc), but Triad's channel model requires a consistent server-side socket abstraction across runtimes. Use @triadjs/fastify for channel support. Tracked in the roadmap.

Comparison with other adapters

  • @triadjs/hono — use when you want edge runtime support (Cloudflare, Deno, Bun) or the minimal Web Fetch API surface.
  • @triadjs/fastify — use for traditional Node servers that need channels/WebSockets, plugins, or Fastify's schema compiler.
  • @triadjs/express — use when integrating Triad into an existing Express app as middleware.