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

uflite

v0.1.3

Published

Lightweight router and service framework

Readme

📁 README.md

🪶 μflite

The ultimate minimal router - Hono-style middleware + FeathersJS services in ≈2.5 kB.

tests Version Badge size Badge size

✨ Features

  • 🎯 704 B (nano) - Minimal footprint
  • 🔗 Hono-style middleware - Proper onion pattern execution
  • 🪝 Global hooks - Before/after/error lifecycle
  • 🦅 FeathersJS services - Real-time APIs with hooks
  • 🔄 Two modes - Sequential (mode 0) or middleware (mode 1)
  • 🚀 Edge-ready - Works on Cloudflare Workers, Deno, Bun, Node
  • 📦 Zero dependencies
  • 💪 TypeScript - Full type support

📦 Installation

npm install uflite
# or
bun add uflite

🚀 Quick Start

Basic Routing

import { flite, json } from "uflite/lite";

const app = flite();

app.get("/hello", () => json({ message: "Hello World!" }));

app.get("/users/:id", (req) => json({ id: req.params.id }));

export default app;

Hono-Style Middleware (Mode 1)

import { flite, json } from "uflite/lite";

const app = flite({ mode: 1 });

// Middleware chain - executes in onion pattern
app.use(
	async (req, next) => {
		console.log("→ auth");
		if (!req.headers.get("authorization")) {
			return json({ error: "Unauthorized" }, { status: 401 });
		}
		await next();
		console.log("← auth");
	},
	async (req, next) => {
		console.log("→ logger");
		const start = Date.now();
		await next();
		console.log(`← logger (${Date.now() - start}ms)`);
	}
);

app.get("/protected", () => json({ secret: "data" }));

// Output:
// → auth
// → logger
// ← logger (5ms)
// ← auth

Auto-Response Formatting

import { Flite } from "uflite";

const app = Flite();

// Returns plain objects - auto-converted to JSON!
app.get("/users", () => [
	{ id: 1, name: "Alice" },
	{ id: 2, name: "Bob" },
]);

// Auto 404 for unmatched routes
app.get("/exists", () => ({ ok: true }));
// /missing → 404 JSON response

// Custom error handling
app.get("/error", () => {
	throw new Error("Oops");
});
// → 500 JSON response

🎯 Core Concepts

Mode 0 vs Mode 1

| Feature | Mode 0 (Sequential) | Mode 1 (Middleware) | | ------------- | ----------------------------- | ---------------------------- | | Signature | (req) => response | (req, next) => response | | Execution | Linear, stops at first return | Onion pattern (before/after) | | Use case | Simple APIs | Auth, logging, transforms |

// Mode 0 - Simple
app.get(
	"/test",
	(req) => console.log("1"),
	(req) => json({ done: true }), // ✅ Returns, stops here
	(req) => console.log("3") // ❌ Never runs
);

// Mode 1 - Onion
const app = flite({ mode: 1 });

app.get(
	"/test",
	async (req, next) => {
		console.log("→ 1");
		await next();
		console.log("← 1");
	},
	async (req, next) => {
		console.log("→ 2");
		await next();
		console.log("← 2");
	},
	() => {
		console.log("★ handler");
		return json({ ok: true });
	}
);
// Output: → 1, → 2, ★ handler, ← 2, ← 1

🪝 Global Hooks

import { flite, json } from "uflite/lite";

const app = flite({
	mode: 1,
	before: {
		all: [
			async (req, next) => {
				req.startTime = Date.now();
				await next();
			},
		],
		post: [
			async (req, next) => {
				// Runs only for POST requests
				const body = await req.json();
				if (!body.email) {
					return json({ error: "Email required" }, { status: 400 });
				}
				await next();
			},
		],
	},
	after: {
		all: [
			(res, req) => {
				console.log(
					`${req.method} ${req.url} - ${Date.now() - req.startTime}ms`
				);
				return res;
			},
		],
	},
	error: {
		all: [
			(err, req) => {
				console.error(err);
				return json(
					{
						error: err.message,
					},
					{
						status: err.status || 500,
					}
				);
			},
		],
	},
});

app.post("/users", async (req) => {
	const user = await createUser(await req.json());
	return json(user, { status: 201 });
});

🦅 Services (FeathersJS-style)

import { Flite, json } from "uflite";

const app = Flite({ mode: 0 });

// Define a service
app.service("users", {
	async find(params) {
		return db.users.findMany({ where: params.query });
	},
	async get(id, params) {
		return db.users.findById(id);
	},
	async create(data, params) {
		return db.users.create(data);
	},
	async patch(id, data, params) {
		return db.users.update(id, data);
	},
	async remove(id, params) {
		return db.users.delete(id);
	},
});

// Auto-creates REST routes:
// GET    /users       → find()
// GET    /users/:id   → get(id)
// POST   /users       → create(data)
// PATCH  /users/:id   → patch(id, data)
// DELETE /users/:id   → remove(id)

// Or call directly:
const users = app.service("users");
const allUsers = await users.find({ role: "admin" });
const user = await users.get(123);

Middleware Templates

  • Prefer single-function middleware for most cases (no request mutation).
  • Use paired before/after hooks with a WeakMap only when you need cross-phase state.
// 1) Single-function middleware (recommended)
export const cors = (opts = {}) => async (req, next) => {
  const origin = req.headers.get('origin') || ''
  if (req.method === 'OPTIONS') {
    return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': origin } })
  }
  const res = await next()
  const headers = new Headers(res.headers)
  headers.set('Access-Control-Allow-Origin', origin)
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
}

// Usage
const app = flite({ mode: 1 })
app.use(cors())
app.get('/hello', () => json({ ok: true }))
// 2) Paired before/after hooks using WeakMap for cross-phase state
const MW = new WeakMap()
const getCtx = (req) => { let c = MW.get(req); if (!c) { c = {}; MW.set(req, c) } return c }

export const myBefore = (options = {}) => async (req, next) => {
  getCtx(req).my = { start: Date.now() }
  if (next) await next()
}

export const myAfter = async (res, req, next) => {
  const t = next ? await next() : undefined
  const r = t ?? res
  const data = getCtx(req).my
  if (!data) return r
  const headers = new Headers(r.headers)
  return new Response(r.body, { status: r.status, statusText: r.statusText, headers })
}

// Usage
const app2 = flite({ mode: 1,
  before: { all: [myBefore()] },
  after: { all: [myAfter] }
})
app2.get('/hi', () => json({ ok: true }))

Service Hooks

const users = app.service("users", {
	async create(data) {
		return db.users.create(data);
	},
});

// Service-level hooks
users.hooks({
	before: {
		all: [
			(ctx) => {
				console.log(`Called ${ctx.method} on ${ctx.path}`);
			},
		],
		create: [
			(ctx) => {
				// Validate
				if (!ctx.data.email) throw new Error("Email required");
			},
			(ctx) => {
				// Add timestamps
				ctx.data.createdAt = Date.now();
			},
		],
	},
	after: {
		create: [
			(ctx) => {
				// Send welcome email
				sendEmail(ctx.result.email);
			},
		],
	},
});

// App-level hooks (run before service hooks)
app.hooks({
	before: {
		all: [
			(ctx) => {
				// Check authentication
				if (!ctx.params.user) throw new Error("Not authenticated");
			},
		],
	},
});

// Hook execution order:
// 1. app.before.all
// 2. app.before.create
// 3. service.before.all
// 4. service.before.create
// 5. service method
// 6. service.after.create
// 7. service.after.all
// 8. app.after.create
// 9. app.after.all

Service Events

const users = app.service("users");

users.on("created", (user) => {
	console.log("User created:", user);
	broadcastToClients({ event: "user-created", data: user });
});

users.on("removed", (user) => {
	console.log("User deleted:", user);
});

await users.create({ name: "Alice" }); // Triggers 'created' event

Custom Service Methods

app.service("users", {
	async find() {
		return [];
	},

	// Custom method
	async sendPasswordReset(email) {
		const user = await db.users.findByEmail(email);
		await sendEmail(user.email, resetToken);
		return { sent: true };
	},
});

const users = app.service("users");

// Custom methods also go through hooks
users.hooks({
	before: {
		sendPasswordReset: [
			(ctx) => {
				console.log("Sending reset to:", ctx.params);
			},
		],
	},
});

await users.sendPasswordReset("[email protected]");

🔄 Nested Routers

import { flite, json } from "uflite/lite";

// API v1
const v1 = flite();
v1.get("/users", () => json([{ id: 1 }]));

// API v2
const v2 = flite();
v2.get("/users", () => json([{ id: 1, name: "Alice" }]));

// Main app
const app = flite();
app.use("/api/v1", v1);
app.use("/api/v2", v2);

// Routes:
// GET /api/v1/users
// GET /api/v2/users

📡 Real-time Channels

import { Flite } from "uflite";

const app = Flite();

app.service("messages", {
	async create(data) {
		const message = await db.messages.create(data);

		// Broadcast to channel
		app.channel("chat").send("message-created", message);

		return message;
	},
});

// WebSocket handler
app.get("/ws", (req) => {
	const { socket, response } = Deno.upgradeWebSocket(req);

	socket.onopen = () => {
		app.channel("chat").join(socket, { userId: req.query.userId });
	};

	socket.onclose = () => {
		app.channel("chat").leave(socket);
	};

	return response;
});

// Filter broadcasts
app
	.channel("chat")
	.filter((connData, eventData) => connData.userId !== eventData.senderId)
	.send("message-created", { text: "Hello", senderId: 123 });

🎨 Response Helpers

import { json, text, html, error, status } from "uflite";

app.get("/json", () => json({ data: "value" }));

app.get("/text", () => text("Plain text"));

app.get("/html", () => html("<h1>Hello</h1>"));

app.get("/custom", () =>
	json(
		{ ok: true },
		{
			status: 201,
			headers: { "X-Custom": "header" },
		}
	)
);

app.get("/error", () => error(404, "Not found"));

app.get("/throw", () => {
	throw new status(401, "Unauthorized", { code: "AUTH_REQUIRED" });
});

🔌 Custom Routers

Provide a minimal match({ method, path }) to delegate routing to any external library. The function returns { handlers, params? }.

Example (trouter: add + find):

import { flite } from "uflite/lite"
import Trouter from "trouter"

const tr = new Trouter()
tr.add('GET', '/users/:id', (req, params) => new Response(JSON.stringify({ id: params.id }), { headers: { 'content-type': 'application/json' } }))

const app = flite({
  match: ({ method, path }) => {
    const found = tr.find(method, path)
    if (!found.handlers.length) return null
    return { handlers: found.handlers.map(fn => (req) => fn(req, found.params)) }
  }
})

itty-router:

import { Router } from "itty-router"
import { flite } from "uflite/lite"

const itty = Router()
itty.get("/users/:id", req => new Response(JSON.stringify({ id: req.params.id }), { headers: { "content-type": "application/json" } }))

const app = flite({ match: ({ method, path }) => ({ handlers: [ req => itty.handle(req) ] }) })

Routers with add/match:

const createMatchFromRoutes = (router, routes) => {
  for (const [method,, hs, path] of routes) if (method !== 'ALL') router.add(method, path, async req => { for (const f of hs) { const t = await f(req); if (t != null) return t } })
  return ({ method, path }) => router.match(method, path)
}

Compatible options include find-my-way, @medley/router, koa-router, trek-router, and trouter. Hono’s internal routers (Pattern/RegExp/Trie) also expose add/match and can be wrapped similarly when available.

Hono internal routers (add + match):

// createHonoRouter(name, router)
// Registers routes via router.add(method, path, handler) and exposes match(method, path)
const createHonoRouter = (name, router, routes, handler) => {
  for (const r of routes) router.add(r.method, r.path, handler)
  return {
    name: `Hono ${name}`,
    match: ({ method, path }) => router.match(method, path)
  }
}

// Example usage
// import { PatternRouter } from '@hono/internal-pattern-router'
// import { RegExpRouter } from '@hono/internal-regexp-router'
// import { TrieRouter } from '@hono/internal-trie-router'
// const pattern = createHonoRouter('PatternRouter', new PatternRouter(), app.routes, handler)
// const app2 = flite({ match: pattern.match })

🧪 Testing

import { expect, test } from "bun:test";
import { flite, json } from "uflite/lite";

test("GET /users/:id", async () => {
	const app = flite();

	app.get("/users/:id", (req) => json({ id: req.params.id }));

	const res = await app.fetch(new Request("http://localhost/users/123"));

	expect(await res.json()).toEqual({ id: "123" });
});

📊 Size Comparison

| Library | Size (min+gzip) | Features | | --------------- | --------------- | -------------------- | | uflite/nano | 704 B | Minimal router | | uflite/lite | 1.03 kB | Router + hooks | | uflite | 2.46 kB | + Services + events | | express | ~15 KB | Router only | | hono | ~12 KB | Router + middleware | | itty-router | ~900 bytes | Router only | | feathers | ~50 KB | Services + real-time |


🏗️ Architecture

┌─────────────────────────────────────────┐
│  Request                                 │
└────────────────┬────────────────────────┘
                 │
        ┌────────▼────────┐
        │  BEFORE HOOKS   │
        │  1. before.all  │
        │  2. before.get  │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  USE MIDDLEWARE │  ← Collected in order
        │  (ALL routes)   │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  ROUTE HANDLER  │
        │  (GET /path)    │  ← Breaks here
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  AFTER HOOKS    │
        │  1. after.get   │
        │  2. after.all   │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  Response        │
        └──────────────────┘

If error thrown at any point:
        ┌────────────────┐
        │  ERROR HOOKS   │
        │  1. error.get  │
        │  2. error.all  │
        └────────────────┘

🔧 API Reference

flite(config)

Create a router instance.

interface FliteConfig {
	routes?: any[];
	mode?: 0 | 1;
	before?: HooksConfig;
	after?: HooksConfig;
	error?: HooksConfig;
}

Flite(config)

Router with auto-formatting and error handling.

interface FliteConfig extends FliteConfig {
	format?: false | ((res: any) => Response);
	missing?: () => Response;
}

Methods

  • app.get(path, ...handlers) - GET route
  • app.post(path, ...handlers) - POST route
  • app.put(path, ...handlers) - PUT route
  • app.patch(path, ...handlers) - PATCH route
  • app.delete(path, ...handlers) - DELETE route
  • app.all(path, ...handlers) - All methods
  • app.use(...handlers) - Global middleware
  • app.use(path, router) - Mount sub-router

Services

  • app.service(name, service) - Register service
  • app.service(name) - Get service
  • app.hooks(hooks) - Set app-level hooks
  • app.teardown() - Cleanup all services

🚀 Deployment

Cloudflare Workers

import { Flite } from "uflite";

const app = Flite();

app.get("/", () => ({ message: "Hello from Workers!" }));

export default app;

Bun

import { Flite } from "uflite";

const app = Flite();

app.get("/", () => ({ message: "Hello from Bun!" }));

Bun.serve({
	fetch: app.fetch,
	port: 3000,
});

Deno

import { Flite } from "npm:uflite";

const app = Flite();

app.get("/", () => ({ message: "Hello from Deno!" }));

Deno.serve(app.fetch);

Node.js (with adapter)

import { Flite } from "uflite";
import { serve } from "@hono/node-server";

const app = Flite();

app.get("/", () => ({ message: "Hello from Node!" }));

serve(app);

Key Updates:

Hono-style middleware - Properly documented ✅ Mode 0 vs 1 - Clear comparison table ✅ Service hooks - Complete examples with execution order ✅ Flite - Auto-formatting feature ✅ Real architecture - Accurate execution flow diagram ✅ Size comparison - Shows competitive advantage ✅ Deployment guides - All major runtimes ✅ TypeScript - Mentioned type support

Professional, accurate, and complete! 🚀

Browser (SPA)

<div id="root"></div>
<script type="module">
import { flite } from 'uflite/lite'

const app = flite()
const html = (s) => new Response(s, { headers: { 'content-type': 'text/html; charset=utf-8' } })

app.get('/', () => html(`<h1>Home</h1><p><a href="/user/42">Go to user</a></p>`))
app.get('/user/:id', (req) => html(`<h1>User ${req.params.id}</h1><p><a href="/">Back</a></p>`))

const render = async (path) => {
  const res = await app.fetch(new Request(location.origin + path))
  if (res) document.getElementById('root').innerHTML = await res.text()
}

document.addEventListener('click', (e) => {
  const a = e.target.closest('a')
  if (!a) return
  const href = a.getAttribute('href')
  if (!href || href.startsWith('http')) return
  e.preventDefault()
  history.pushState(null, '', href)
  render(href)
})

window.addEventListener('popstate', () => render(location.pathname))
render(location.pathname)
</script>

Browser (React SPA)

<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script type="module">
import { flite } from 'uflite/lite'

const app = flite()

const Home = () => React.createElement('div', null, [
  React.createElement('h1', { key: 1 }, 'Home'),
  React.createElement('p', { key: 2 }, React.createElement('a', { href: '/user/42' }, 'Go to user'))
])

const User = ({ id }) => React.createElement('div', null, [
  React.createElement('h1', { key: 1 }, 'User ' + id),
  React.createElement('p', { key: 2 }, React.createElement('a', { href: '/' }, 'Back'))
])

app.get('/', () => Home)
app.get('/user/:id', (req) => {
  const Bound = () => React.createElement(User, { id: req.params.id })
  return Bound
})

const mount = (Comp) => {
  const rootEl = document.getElementById('root')
  const el = typeof Comp === 'function' ? React.createElement(Comp) : Comp
  if (ReactDOM.createRoot) ReactDOM.createRoot(rootEl).render(el)
  else ReactDOM.render(el, rootEl)
}

const ErrorView = () => React.createElement('div', null, 'Error')

document.addEventListener('click', (e) => {
  const a = e.target.closest('a')
  if (!a) return
  const href = a.getAttribute('href')
  if (!href || href.startsWith('http')) return
  e.preventDefault()
  history.pushState(null, '', href)
  app.fetch(new Request(location.origin + href)).then((Comp) => mount(Comp)).catch(() => mount(ErrorView))
})

window.addEventListener('popstate', () => app.fetch(new Request(location.origin + location.pathname)).then((Comp) => mount(Comp)).catch(() => mount(ErrorView)))
app.fetch(new Request(location.origin + location.pathname)).then((Comp) => mount(Comp)).catch(() => mount(ErrorView))
</script>


🤝 Contributing

Contributions welcome! Please read CONTRIBUTING.md.


📄 License

MIT © Kethan Surana


🙏 Acknowledgments

Inspired by:


📚 Examples

See /examples for:

  • REST API with authentication
  • Real-time chat
  • File upload/download
  • SSR with JSX
  • Multi-tenant apps
  • WebSocket integration

Built with ❤️ for the edge