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

@bethel-nz/sumi

v1.0.3

Published

A blazing fast web framework built on Hono with file-based routing, OpenAPI support, and CLI tooling

Readme

Sumi

Sumi is a file-based routing and configuration layer for Hono, designed to streamline backend development on the Bun runtime. It integrates OpenAPI generation, Zod validation, and a command-line interface to provide a structured development experience.

Choose Sumi if you require Hono's performance but need a more structured, convention-over-configuration approach for your project.

Core Features

  • File-based Routing: Maps directory structure to URL paths (e.g., routes/users/[id].ts -> /users/:id).
  • Integrated OpenAPI Generation: Automatically generates an openapi.json specification from your routes and Zod schemas.
  • Schema-driven Validation: Leverages Zod for compile-time and runtime validation of requests (params, query, JSON body).
  • Command-Line Interface: Includes tools for project scaffolding, code generation, and running the development server.
  • Hot Reload: The development server (sumi dev) monitors file changes and reloads automatically.
  • Environment Validation: Enforces required environment variables and validates their types on startup.
  • Middleware System: Supports directory-level (global, nested) and per-route middleware.

Quick Start

1. Create a new project

# Using bunx (recommended)
bunx @bethel-nz/sumi new my-api

2. Start the development server

cd my-api
bun install
sumi dev

The server will be available at http://localhost:3000, with the following endpoints enabled by default:

  • /docs: Interactive API documentation (Scalar UI).
  • /openapi.json: The raw OpenAPI specification.
  • Hot reloading is active.

Project Structure

A new Sumi project uses the following directory structure:

my-api/
├── routes/                    # API routes (file-based)
│   ├── index.ts              # Handles GET /
│   └── users/
│       ├── index.ts          # Handles GET/POST /users
│       └── [id].ts           # Handles GET/PUT/DELETE /users/:id
├── middleware/               # Reusable middleware
│   ├── _index.ts             # Applied to all routes in the project
│   └── auth.ts               # Example route-specific middleware
└── sumi.config.ts           # Main configuration file

Route Examples

Basic Route

A file's exported default object defines the handlers for each HTTP method.

// routes/hello.ts
import { createRoute } from '@bethel-nz/sumi/router';

export default createRoute({
  get: (c) => c.json({ message: 'Hello World!' }),
});

Validated Route with OpenAPI

By adding a schema and openapi block, you enable validation and documentation.

// routes/users/index.ts
import { z } from 'zod';
import { createRoute } from '@bethel-nz/sumi/router';

const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

export default createRoute({
  // This route does not define a schema, but provides OpenAPI metadata.
  get: {
    openapi: {
      summary: 'Get all users',
      responses: {
        '200': {
          description: 'List of users',
          content: {
            'application/json': { schema: z.array(userSchema) },
          },
        },
      },
    },
    handler: (c) => c.json([{ name: 'John', email: '[email protected]' }]),
  },
  // This route validates the incoming JSON body against the schema.
  post: {
    schema: { json: userSchema },
    openapi: {
      summary: 'Create a user',
      responses: {
        '201': {
          description: 'User created',
          content: {
            'application/json': { schema: userSchema },
          },
        },
      },
    },
    handler: (c) => {
      // c.req.valid('json') returns the validated and typed data.
      const userData = c.req.valid('json');
      return c.json(userData, 201);
    },
  },
});

Dynamic Route

Bracketed filenames ([param]) create dynamic routes. The parameter is available via c.req.param().

// routes/users/[id].ts
import { createRoute } from '@bethel-nz/sumi/router';

export default createRoute({
  get: (c) => {
    const userId = c.req.param('id');
    return c.json({ userId });
  },
});

Middleware System

Global and Scoped Middleware

Middleware is applied based on its location. A file named _index.ts in the middleware directory applies to all routes. A file named _index.ts in middleware/admin/ would apply to all routes inside a corresponding routes/admin/ directory.

// middleware/_index.ts
import { createMiddleware } from '@bethel-nz/sumi/router';

// This middleware will run for every request.
export default createMiddleware({
  _: async (c, next) => {
    const start = Date.now();
    await next();
    const duration = Date.now() - start;
    console.log(
      `HTTP ${c.req.method} ${c.req.url} - ${c.res.status} (${duration}ms)`
    );
  },
});

Route-Specific Middleware

Individual middleware files can be applied explicitly in a route's configuration.

1. Define the Middleware

// middleware/auth.ts
import { createMiddleware } from '@bethel-nz/sumi/router';

export default createMiddleware({
  _: async (c, next) => {
    const authHeader = c.req.header('Authorization');
    if (!authHeader?.startsWith('Bearer ')) {
      return c.json({ error: 'Unauthorized' }, 401);
    }
    // Add data to the context for downstream handlers.
    c.set('user', { id: '123', role: 'admin' });
    await next();
  },
});

2. Apply it in a Route

Reference the middleware by its filename (without the extension) in the middleware array.

// routes/admin/users.ts
import { createRoute } from '@bethel-nz/sumi/router';

export default createRoute({
  get: {
    // Apply the 'auth' middleware to this specific endpoint.
    middleware: ['auth'],
    openapi: {
      summary: 'Get admin users',
      security: [{ bearerAuth: [] }],
    },
    handler: (c) => {
      // Retrieve context data set by the middleware.
      const user = c.get('user');
      return c.json({ message: `Admin data for user ${user.id}` });
    },
  },
});

Configuration

The sumi.config.ts file centralizes project-wide settings.

// sumi.config.ts
import { defineConfig } from '@bethel-nz/sumi';
import { z } from 'zod';

export default defineConfig({
  port: 3000,

  // Defines environment variables required by the application.
  // Missing or invalid variables will cause an error on startup.
  env: {
    schema: {
      DATABASE_URL: z.string().url(),
      JWT_SECRET: z.string().min(32),
      NODE_ENV: z.enum(['development', 'production']).default('development'),
    },
    // Specify which variables are mandatory in a production environment.
    required: ['DATABASE_URL'],
  },

  // Serves static files from the specified directory.
  static: [{ path: '/public/*', root: './public' }],

  // Configuration for generated OpenAPI specification.
  openapi: {
    info: {
      title: 'My API',
      version: '1.0.0',
      description: 'API for my application',
    },
  },

  // Configuration for the Scalar documentation UI.
  // This requires the 'openapi' configuration to be present.
  docs: {
    path: '/docs', // Custom URL for the docs page.
    theme: 'purple',
    pageTitle: 'My API Docs',
  },

  // Application lifecycle hooks.
  hooks: {
    onReady: () => console.log('Server is ready.'),
    onShutdown: () => console.log('Server is shutting down.'),
    onError: (error, c) =>
      console.error(`Error on ${c.req.path}:`, error.message),
  },
});

CLI Commands

# Start dev server with hot reload
sumi dev

# Start production server
sumi start

# Create a new Sumi project
sumi new <project-name>

# Generate a route file
sumi generate route users/[id] --methods get,put,delete

# Generate a middleware file
sumi generate middleware rate-limiter

Testing

Sumi provides a test utility that loads your application instance for use in tests.

// tests/users.test.ts
import { createTestApp } from '@bethel-nz/sumi/testing';
import { test, expect } from 'bun:test';

test('GET /users should return an array', async () => {
  // createTestApp loads sumi.config.ts and builds the route tree.
  const app = await createTestApp();

  const response = await app.request('/users');
  expect(response.status).toBe(200);

  const body = await response.json();
  expect(Array.isArray(body)).toBe(true);
});

Environment Variables

Access validated environment variables from the context in any route handler.

// In any route file
import { createRoute } from '@bethel-nz/sumi/router';

export default createRoute({
  get: (c) => {
    // c.get('env') provides the validated & typed environment object.
    const nodeEnv = c.get('env').NODE_ENV;
    return c.json({ current_environment: nodeEnv });
  },
});

Framework Comparison

| Feature | Sumi | Hono | Express | Fastify | | ----------------------- | ---- | ---- | ------- | ------- | | File-based routing | ✅ | ❌ | ❌ | ❌ | | Auto OpenAPI docs | ✅ | 🔧 | 🔧 | 🔧 | | Built-in Zod validation | ✅ | 🔧 | ❌ | 🔧 | | TypeScript-first | ✅ | ✅ | 🔧 | 🔧 | | CLI tooling | ✅ | ❌ | ❌ | ❌ |

🔧: Requires manual integration or plugins.

License

MIT