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

@literallyjoel/router

v1.0.6

Published

Bun router/controllers with Standard Schema validation

Readme

@literallyjoel/router

A minimal filesystem router + controller framework for Bun that:

  • Uses Bun’s native route matching (serve({ routes })) — no custom matcher on the hot path
  • Autoloads controllers from files like get.ts, post.ts, etc.
  • Validates request JSON via Standard Schema V1 (Zod, Valibot, ArkType, Yup, Joi, Effect Schema, …)
  • Optionally extracts route params from bracketed paths (e.g., /users/[id]) without replacing Bun’s router
  • Supports UUID path param validation and optional authentication
  • Returns consistent JSON errors

Standard Schema: https://standardschema.dev

Installation

bun add @literallyjoel/router
# or
npm install @literallyjoel/router

Install any Standard Schema-compatible validator you prefer (Zod, Valibot, ArkType, Yup, Joi, etc.).

Quick Start (Bun’s routes)

import { serve } from "bun";
import index from "@app/index.html";
import { getRoutes } from "@literallyjoel/router";

const routes = await getRoutes({
  routesDirectory: "./src/routes",
  routePrefix: "/api", // optional; you can also include 'api' in your directory structure
  authProvider: {
    getSession: async (headers) => {
      const token = headers.get("authorization");
      return token ? { user: { id: "123" } } : null;
    },
  },
  logger: {
    error: (message, meta) => console.error(message, meta),
  },
  [id]`
});

const server = serve({
  routes: {
    // Serve index.html for all unmatched routes.
    "/*": index,

    // Default 404 for unmatched API paths
    "/api/*": new Response("Not Found", { status: 404 }),

    // Autogenerated API endpoints from the filesystem:
    ...routes,
  },

  development: process.env.NODE_ENV !== "production" && {
    hmr: true,
    console: true,
  },

  port: 8181,
});

console.log(`🚀 Server running at ${server.url}`);

File structure

getRoutes() discovers controllers from filenames:

src/
  routes/
    users/
      get.ts      → GET /api/users
      post.ts     → POST /api/users
    users/[id]/
      get.ts      → GET /api/users/[id]
  • Use routePrefix to mount under a base path (e.g., /api).
  • Bracketed segments like [id] are recognized and, when enabled, params are extracted at runtime and attached to req.params.

Controllers

Create controllers with createController(handler, config, additionalValidator?).

  • validationSchema: any Standard Schema V1-compliant schema (Zod, Valibot, ArkType, Yup, Joi, …)
  • requiresAuthentication: boolean
  • validateUUIDs: string[] of param keys to validate as UUID
  • authProvider: override per-controller if desired (otherwise uses the one from getRoutes)

Example (Zod):

// src/routes/users/post.ts
import { createController } from "@literallyjoel/router";
import { z } from "zod";

const Schema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
});

export default createController(
  async (ctrl) => {
    return Response.json({ user: ctrl.json });
  },
  {
    validationSchema: Schema, // Standard Schema via Zod
    requiresAuthentication: false,
  },
  () => []
);

Valibot:

import { createController } from "@literallyjoel/router";
import * as v from "valibot";

const Schema = v.object({
  username: v.string([v.minLength(3)]),
  email: v.string([v.email()]),
});

export default createController(
  async (ctrl) => Response.json({ user: ctrl.json }),
  { validationSchema: Schema, requiresAuthentication: false },
  () => []
);

ArkType:

import { createController } from "@literallyjoel/router";
import { type } from "arktype";

const Schema = type({ username: "string.min(3)", email: "string.email" });

export default createController(
  async (ctrl) => Response.json({ user: ctrl.json }),
  { validationSchema: Schema, requiresAuthentication: false },
  () => []
);

Params and UUIDs

  • When params extraction is enabled (default), bracketed paths like /users/[id] will populate req.params = { id: "..." }.
  • Controllers can ask to validate specific params as UUIDs:
export default createController(
  async (ctrl) => Response.json({ id: ctrl.params.userId }),
  {
    requiresAuthentication: false,
    validateUUIDs: ["userId"],
  },
  () => []
);

Notes:

  • Bun’s native routes map doesn’t populate req.params. This package adds params only for paths discovered with bracket segments, keeping the overhead minimal (compiled once at boot; O(segments) match per request for those routes only).
  • Static routes incur zero param-extraction overhead.

Authentication

Provide an authProvider in getRoutes() to enable sessions for all routes:

authProvider: {
  getSession: async (headers) => {
    const token = headers.get("authorization");
    return token ? { user: { id: "123" } } : null;
  },
}

If a controller sets requiresAuthentication: true and session is missing, the controller responds with 401 automatically.

Validation (Standard Schema)

This library accepts any validator that implements Standard Schema V1:

  • The schema must expose ~standard.validate(value) and version: 1.
  • validate returns { value } on success or { issues } on failure (sync or async).
  • Validation failures are returned as ValidationError (400) with a standardized fields array.

Example error response:

{
  "message": "Bad Request",
  "fields": [
    { "field": "email", "message": "Invalid email" }
  ]
}

Built-in errors

  • ValidationError (400)
  • UnauthorizedError (401)
  • ForbiddenError (403)
  • NotFoundError (404)
  • ConflictError (409)
  • InternalServerError (500)

Usage:

throw new NotFoundError({ message: "User not found" });

API

  • getRoutes(options)

    • routesDirectory: string
    • routePrefix?: string (prefix all discovered routes, e.g., /api)
    • authProvider?: { getSession(headers): Promise<any | null> }
    • logger?: { error(message, meta?) }
  • createController(handler, config, additionalValidator?)

    • validationSchema?: StandardSchemaV1<any, TData>
    • validateUUIDs?: string[]
    • requiresAuthentication: boolean
    • authProvider?: AuthProvider
  • BaseController

    • request: BunRequest
    • ctx: HandlerContext
    • json: TData (validated)
    • params: Record<string, string> (validated UUIDs if configured)
    • session, user

Build & Publish

bun build ./src --outdir ./dist
npm publish --access public

package.json should include:

{
  "type": "module",
  "main": "./dist/index.js",
  "exports": { ".": "./dist/index.js" }
}

Notes

  • Validation uses Standard Schema V1 (sync or async). Issues are mapped to field errors.
  • Relies on Bun’s routes map for dispatch. This package only discovers controllers and wires handlers.
  • Param extraction is runtime-only for bracketed paths; static routes have zero overhead.