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

@routegraph/elysia

v1.0.0

Published

Elysia adapter for RouteGraph, Bun-native (Node via @elysiajs/node).

Readme

@routegraph/elysia

Elysia adapter for RouteGraph — Bun-native, but also runnable on Node via @elysiajs/node.

Installation

pnpm add @routegraph/elysia @routegraph/core elysia zod

elysia is a peer dependency (>=1.0.0). If you want to run under plain Node (as this monorepo's own example does) rather than Bun, also install @elysiajs/node and pass it as Elysia's adapter.

createElysiaPlugin(graph)

function createElysiaPlugin(graph: RouteGraph): Promise<Elysia>

This is async — unlike every other adapter's synchronous factory, you must await it before mounting. See why below.

Usage

import { Elysia } from 'elysia'
import { node } from '@elysiajs/node'   // omit this + the adapter option to run on Bun directly
import { RouteGraph } from '@routegraph/core'
import { createElysiaPlugin } from '@routegraph/elysia'

const graph = new RouteGraph({ routesDir: './routes' })
await graph.load()

const app = new Elysia({ adapter: node() })

const apiPlugin = await createElysiaPlugin(graph)
app.group('/api', (group) => group.use(apiPlugin))

app.onError(({ error, set }) => {
  set.status = 500
  return { error: 'Internal server error' }
})

app.listen(3000)

Elysia response pattern explanation

Every other RouteGraph adapter's framework lets you write a response imperatively — Express's res.json(data), Fastify's reply.send(data), Koa's ctx.body = data all take effect the moment they're called. Elysia's own convention is the opposite: a handler's return value becomes the response, and there's no "send now" primitive to call from inside RouteGraph's cross-adapter NormalizedResponse contract (.status(), .json(), .send(), .setHeader(), .end() — all synchronous, void-returning, and callable more than once, e.g. res.status(404).json(...)).

ElysiaResponseCollector (in src/adapter.ts) implements NormalizedResponse by buffering whatever status/body/headers your handler (or the adapter's own validation-failure/error paths) set, instead of writing them anywhere immediately. Once your handler and the middleware chain have both finished, the plugin's request wrapper applies the buffered status/headers to ctx.set and returns the buffered body as the handler's actual return value — which is what makes it appear in Elysia's response. This buffering is also why createElysiaPlugin itself has to be async: constructing the plugin and wiring up its per-route handlers needs to happen before .listen(), but the response-collection logic runs per-request, independent of that.

See DECISIONS.md for the full reasoning.

Middleware adaptation notes

Middleware functions ((req, res, next) => Promise<void>) work unmodified — they call res.status()/res.json() on the same ElysiaResponseCollector instance the handler receives, so a middleware that short-circuits by not calling next() still ends up producing the right buffered response.

Validation error format

{ "error": "Validation failed", "issues": [{ "field": "body.name", "message": "String must contain at least 1 character(s)", "code": "too_small" }] }

Docs UI

routegraph dev's automatic docs-UI mounting is not implemented for Elysia yet — see examples/with-express/index.elysia.ts for the manual bridging pattern used there (wrapping createDocsMiddleware's Connect-style signature to return a real Response).