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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@reono/root

v0.0.1

Published

Reono monorepo root package

Downloads

5

Readme

Reono

Build HTTP APIs with JSX. Define routes, middleware, and validation using familiar JSX syntax, powered by a high‑performance runtime and pluggable server adapters.

Status: Maturing. The core runtime, trie router, validation, and Node.js adapter are stable and well‑tested. Real‑world examples (Scenario‑1 and a Multi‑Tenant SaaS API Gateway in Scenario‑2) are included. Expect iterative improvements as we head toward v2.

Why Reono?

  • JSX‑first API design: Define routers, routes, and middleware declaratively
  • Type‑safe: First‑class TypeScript with rich inference via schema validation
  • Fast: Optimized trie‑based routing with O(1) average case matching
  • Standards‑based: Built on Web APIs (Request, Response, Headers)
  • Framework‑agnostic core, pluggable server adapters (Node HTTP available)
  • Composable middleware with simple, predictable control‑flow

Monorepo at a glance

  • packages/core — Core JSX runtime, router, utilities, and tests
  • packages/node-server — Node HTTP adapter (@reono/node-server)
  • apps/scenario-1 — Minimal example showing routing, middleware, and validation
  • apps/scenario-2 — Multi‑Tenant SaaS API Gateway (authZ/authN, tiered rate limiting, dynamic CORS, analytics & billing)
  • apps/api — Additional sample/tests exercising core behavior
  • packages/ui, packages/eslint-config, packages/typescript-config — Internal tooling and DX

Quick start

Install

npm install reono @reono/node-server
# or
pnpm add reono @reono/node-server
# or
yarn add reono @reono/node-server

Basic example

import { createApp } from "@reono/node-server";
import { z } from "zod";

// Define your API with JSX
const App = () => (
  <router path="api/v1">
    <get path="hello" handler={(c) => c.json({ message: "Hello, World!" })} />

    <router path="users">
      <get path="" handler={(c) => c.json(getAllUsers())} />
      <post
        path=""
        validate={{
          body: z.object({ name: z.string(), email: z.string().email() }),
        }}
        handler={(c) => c.json(createUser(c.body))}
      />
      <get
        path=":id"
        validate={{ params: z.object({ id: z.coerce.number() }) }}
        handler={(c) => c.json(getUser(c.params.id))}
      />
    </router>
  </router>
);

// Create and start server
const app = createApp();
app.serve(<App />);
app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

TypeScript configuration

Configure your tsconfig.json for JSX:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "reono"
  }
}

Real‑world examples

  • Scenario‑1: A focused starter demonstrating routing, validation, and middleware. See apps/scenario-1.
  • Scenario‑2: A comprehensive Multi‑Tenant SaaS API Gateway. See apps/scenario-2 for code, middleware, routers, and tests.

SaaS API Gateway (Scenario‑2) highlights

  • Multi‑tenant authentication, authorization, and request context
  • Tiered rate limiting (Free, Premium, Enterprise) with per‑tenant isolation
  • Dynamic CORS based on tenant configuration
  • Feature routers: tenants, users, analytics, billing, and content
  • Exhaustive integration + performance tests validating behavior and SLAs

Refer to the app’s README for local run instructions, example API keys, and test commands.

Core concepts

JSX elements

Reono provides intrinsic JSX elements for defining your API structure:

<router>

Groups routes under a common path prefix. Supports nesting for complex API structures.

<router path="api/v1">
  <router path="users">{/* Routes nested under /api/v1/users */}</router>
</router>

HTTP method elements

Define route handlers for specific HTTP methods with full type safety.

<get path="users" handler={(c) => c.json(users)} />
<post path="users" validate={{ body: userSchema }} handler={createUser} />
<put path="users/:id" handler={updateUser} />
<delete path="users/:id" handler={deleteUser} />
<patch path="users/:id" handler={patchUser} />

<use> — middleware

Apply middleware to routes with clean composition. Middleware runs in declaration order.

<use handler={authMiddleware}>
  <use handler={loggingMiddleware}>
    <get path="protected" handler={protectedHandler} />
  </use>
</use>

Path patterns

{/* Static */}
<get path="users" />

{/* Params (captured in c.params) */}
<get path="users/:id" />
<get path="users/:userId/posts/:postId" />

{/* Wildcards (matches remaining path) */}
<get path="files/*" />

Request context

type ApiContext = {
  params: Record<string, any>;
  body: any;
  req: Request;
  url: URL; // Parsed request URL
  state: Map<string, any>; // Per-request state bag
  res?: Response;
  json: (data: unknown, init?: ResponseInit) => Response;
};

Validation & type safety

Use any validation library with a parse method (e.g. Zod) for runtime validation and compile‑time inference:

import { z } from "zod";

const userSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(0).optional(),
});

<post
  path="users"
  validate={{
    body: userSchema,
    params: z.object({ id: z.coerce.number() }),
  }}
  handler={(c) => c.json(createUser(c.body), 201)}
/>;

Middleware system

Create reusable middleware with the Koa‑style (context, next) pattern:

import { type MiddlewareHandler } from "reono";

const logger: MiddlewareHandler = async (c, next) => {
  const start = Date.now();
  const result = await next();
  console.log(`← ${c.req.method} ${c.req.url} (${Date.now() - start}ms)`);
  return result;
};

const auth: MiddlewareHandler = async (c, next) => {
  const token = c.req.headers.get("authorization");
  if (!token) return new Response("Unauthorized", { status: 401 });
  return next();
};

Complete example

import { createApp } from "@reono/node-server";
import { type MiddlewareHandler, CORS } from "reono";
import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  role: z.enum(["user", "admin"]).default("user"),
});
const userUpdateSchema = userSchema.partial();

let users = [
  { id: 1, name: "Alice", email: "[email protected]", role: "admin" },
  { id: 2, name: "Bob", email: "[email protected]", role: "user" },
];

const logger: MiddlewareHandler = async (c, next) => {
  const start = Date.now();
  const result = await next();
  console.log(`← ${c.req.method} ${c.req.url} (${Date.now() - start}ms)`);
  return result;
};

const getAllUsers = (c) => c.json(users);
const getUser = (c) => {
  const user = users.find((u) => u.id === c.params.id);
  if (!user) return new Response("User not found", { status: 404 });
  return c.json(user);
};
const createUser = (c) => {
  const newUser = { id: Date.now(), ...c.body };
  users.push(newUser);
  return c.json(newUser, 201);
};
const updateUser = (c) => {
  const index = users.findIndex((u) => u.id === c.params.id);
  if (index === -1) return new Response("User not found", { status: 404 });
  users[index] = { ...users[index], ...c.body };
  return c.json(users[index]);
};
const deleteUser = (c) => {
  const index = users.findIndex((u) => u.id === c.params.id);
  if (index === -1) return new Response("User not found", { status: 404 });
  users.splice(index, 1);
  return new Response(null, { status: 204 });
};

const App = () => (
  <CORS origins={["*"]} headers={["Content-Type", "Authorization"]}>
    <use handler={logger}>
      <router path="api/v1">
        <get
          path="health"
          handler={(c) => c.json({ status: "ok", timestamp: Date.now() })}
        />
        <router path="users">
          <get path="" handler={getAllUsers} />
          <get
            path=":id"
            validate={{ params: z.object({ id: z.coerce.number() }) }}
            handler={getUser}
          />
          <post path="" validate={{ body: userSchema }} handler={createUser} />
          <put
            path=":id"
            validate={{
              params: z.object({ id: z.coerce.number() }),
              body: userUpdateSchema,
            }}
            handler={updateUser}
          />
          <delete
            path=":id"
            validate={{ params: z.object({ id: z.coerce.number() }) }}
            handler={deleteUser}
          />
        </router>
      </router>
    </use>
  </CORS>
);

const app = createApp();
app.serve(<App />);
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000;
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
});

Intrinsic elements and props

  • <router path>: groups child routes under a path prefix. path can be string or array. Supports nested routers.
  • <use handler>: middleware. Runs outer‑to‑inner and unwinds inner‑to‑outer. Multiple nested use stack.
  • HTTP routes: <get|post|put|delete|patch path handler validate?>
    • path supports:
      • Static: users
      • Params: :id (captured in c.params, optionally coerced via validate.params)
      • Wildcard: * (consumes the remainder)
    • validate (optional): { body?, query?, params?, headers? } where each is a schema‑like object with parse(input) (e.g. Zod). On success, values are replaced in c.body, c.params, etc. On failure, the runtime responds 400 with a JSON error payload.

Runtime semantics

  • Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD. Only the five route tags are supported in JSX; OPTIONS/HEAD return 405 unless explicitly routed.
  • Matching & normalization:
    • Leading/trailing/multiple slashes are normalized.
    • Static segments take precedence over params at the same depth (/users/me beats /users/:id).
    • * wildcard matches the remainder.
  • 404 vs 405:
    • Unknown path → 404 Not Found.
    • Known path without a handler for the method → 405 Method Not Allowed (plain text).
  • Middleware composition:
    • Koa‑style (ctx, next); order is declaration order.
    • Calling next() multiple times is guarded (500).
    • Middleware may short‑circuit by returning a Response.
  • Body parsing (by Content-Type):
    • application/json → parsed JSON (invalid JSON → 400).
    • text/* → string via req.text().
    • application/x-www-form-urlencoded → plain object from FormData.
  • Context and responses:
    • c includes { params, body, req, url, state, res?, json(data, init?) }.
    • c.json() sets content-type: application/json; charset=utf-8 and optional status.
    • Handlers may return a Response directly; it is passed through unchanged.
    • If no handler or middleware writes a response, a default 200 with JSON null is returned.

Utilities (sugar elements)

Convenience components built on top of <use> for common patterns. Import from reono:

import { CORS, RateLimit, Guard, Static, FileUpload, Transform } from "reono";

CORS

Preflight + headers with a single wrapper (OPTIONS routes are injected automatically for matched paths):

<CORS
  origins={["https://app.example.com", "https://admin.example.com"]}
  methods={["GET", "POST", "PUT", "DELETE"]}
  headers={["Content-Type", "Authorization"]}
  credentials
  maxAge={86400}
>
  <router path="api">{/** your routes */}</router>
</CORS>

RateLimit

Apply rate limits per IP or custom key (e.g., API key or tenant ID). Headers are added to responses automatically:

<RateLimit
  requests={100}
  window={60_000}
  keyGen={(c) =>
    c.req.headers.get("x-api-key") ??
    c.req.headers.get("x-forwarded-for") ??
    "anonymous"
  }
>
  <router path="api">{/** rate‑limited routes */}</router>
</RateLimit>

Guard

Conditionally allow access. Useful for auth checks and feature flags:

<Guard
  condition={(c) => c.state.get("user")?.role === "admin"}
  fallback={(c) => c.json({ error: "Admin required" }, 403)}
>
  <router path="admin">{/** admin routes */}</router>
</Guard>

Transform

Post‑process responses (add headers, wrap payloads, etc.):

<Transform
  transform={(out) =>
    out instanceof Response
      ? out
      : { data: out, requestId: crypto.randomUUID() }
  }
>
  <router path="api">{/** all responses wrapped */}</router>
</Transform>

Static

Serve static assets at a mount point; can be wrapped with auth middleware:

<Static path="/assets" directory="./public" />

<Static path="/uploads" directory="./uploads" middleware={[authMiddleware]} />

FileUpload

Validate uploads (size and type) and access parsed files from c.state:

<FileUpload
  maxSize={10 * 1024 * 1024}
  allowedTypes={["image/jpeg", "image/png"]}
>
  <post
    path="upload"
    handler={(c) => c.json({ uploaded: c.state.get("uploadedFileCount") })}
  />
</FileUpload>

See tested examples in packages/core/__tests__/utilities/ and Scenario‑2 for multi‑tenant patterns using these utilities together.

Adapter: Node HTTP (@reono/node-server)

  • createApp() returns { serve(element), listen(port, cb?), close() }.
  • serve(element) builds the trie and stores a fetch‑compatible handler.
  • listen(port) wires Node’s http.createServer to translate IncomingMessage → Fetch Request and write back the Fetch Response.

Testing

The repository includes extensive tests:

  • Core/unit tests in packages/core/__tests__
  • Example‑level integration and performance tests in apps/scenario-1 and apps/scenario-2
  • Additional sample tests in apps/api

See each app’s README for scripts and commands to run locally. All scenarios are wired to validate 404/405 behavior, normalization, validation, middleware semantics, and performance characteristics.

Roadmap

  • Developer experience: richer response helpers (text, HTML, redirects, streaming), query/headers/cookies, better error messages
  • Ecosystem: more utility components, static/caching improvements
  • Adapters: Bun, Deno, Cloudflare Workers, Vercel Edge
  • OpenAPI: schema generation and docs tooling
  • Performance: streaming, caching, compression

License

ISC

Reono — Build HTTP APIs with the power and familiarity of JSX.