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

create-bhaze

v1.0.7

Published

<div align="center">

Readme

⚡ bhaze

Bun · Hono · Arc · Zod · Ecosystem

A fast, opinionated, production-ready backend starter kit.
Zero bloat. Strict types. Clean architecture.

Bun Hono Prisma Zod TypeScript


Table of Contents


Stack

| Layer | Tool | Version | Purpose | | ---------- | ------------------- | -------- | ----------------------------------- | | Runtime | Bun | ^1.x | JS runtime, package manager, runner | | Framework | Hono | ^4.7 | Ultrafast web framework | | ORM | Prisma | ^6.0 | Type-safe DB access | | Validation | Zod | ^3.24 | Schema validation + type inference | | Middleware | @hono/zod-validator | ^0.4 | Route-level Zod integration | | Language | TypeScript | latest | Full strict mode | | Formatter | Prettier | latest | Opinionated code formatting |


Project Structure

bhaze/
├── prisma/
│   └── schema.prisma           # Database schema
│
├── src/
│   ├── index.ts                # App entry point — Hono init, middleware, server export
│   │
│   ├── config/
│   │   └── env.ts              # Zod-validated env variables — crashes early if invalid
│   │
│   ├── routes/
│   │   ├── index.ts            # Root router — registers all sub-routers under /api
│   │   └── user.routes.ts      # User resource routes with zValidator middleware
│   │
│   ├── controllers/
│   │   └── user.controller.ts  # HTTP layer only — reads request, returns response
│   │
│   ├── services/
│   │   └── user.service.ts     # Business logic + all Prisma DB calls
│   │
│   ├── middlewares/
│   │   └── error.middleware.ts # Global error handler — catches Zod + generic errors
│   │
│   ├── validators/
│   │   └── user.validator.ts   # Zod schemas + inferred TypeScript types
│   │
│   ├── lib/
│   │   ├── db.ts               # Prisma singleton with environment-aware logging
│   │   └── response.ts         # Typed response helpers: ok, created, fail, notFound…
│   │
│   └── types/                  # Shared TypeScript interfaces and utility types
│
├── .env.example                # Required env variables template
├── .prettierrc                 # Prettier formatting rules
├── .prettierignore             # Files excluded from formatting
├── bunfig.toml                 # Bun runtime config + path alias resolution
├── tsconfig.json               # Strict TypeScript config
└── package.json

Getting Started

Prerequisites

  • Bun >= 1.0curl -fsSL https://bun.sh/install | bash
  • A running PostgreSQL instance (or swap to SQLite for local dev)

Installation

# 1. Clone or use as template
git clone https://github.com/yourname/bhaze.git
cd bhaze

# 2. Install dependencies
bun install

# 3. Set up environment
cp .env.example .env
# → Edit .env and fill in DATABASE_URL

# 4. Generate Prisma client
bun db:generate

# 5. Run migrations
bun db:migrate

# 6. Start development server
bun dev

Server starts at http://localhost:3000.
Health check: GET http://localhost:3000/health


Environment Variables

Defined and validated in src/config/env.ts using Zod.
The app will not start if any required variable is missing or malformed — fail fast, not silently.

# .env.example

PORT=3000
DATABASE_URL="postgresql://user:password@localhost:5432/bhaze_db"
NODE_ENV=development   # development | production | test

| Variable | Required | Default | Description | | -------------- | -------- | ------------- | --------------------------- | | DATABASE_URL | ✅ Yes | — | Prisma DB connection string | | PORT | ❌ No | 3000 | HTTP server port | | NODE_ENV | ❌ No | development | Runtime environment |

Never commit .env to version control. Only .env.example is tracked.


Path Aliases

Configured in both tsconfig.json (TypeScript resolution) and bunfig.toml (Bun runtime resolution). Both are required — tsconfig alone only satisfies the type checker, not the actual runtime.

| Alias | Resolves to | | ---------------- | ------------------- | | @/* | src/* | | @routes/* | src/routes/* | | @controllers/* | src/controllers/* | | @services/* | src/services/* | | @middlewares/* | src/middlewares/* | | @validators/* | src/validators/* | | @lib/* | src/lib/* | | @types/* | src/types/* | | @config/* | src/config/* |

// ✅ Use this
import { UserService } from "@services/user.service";
import { ok, notFound } from "@lib/response";

// ❌ Not this
import { UserService } from "../../services/user.service";

API Architecture

Request flow is strictly one-directional. No layer skips another.

HTTP Request
     │
     ▼
  Route            → zValidator middleware validates body/params against Zod schema
     │
     ▼
  Controller       → Reads validated request, calls service, returns response helper
     │
     ▼
  Service          → All business logic and Prisma DB calls live here
     │
     ▼
  Prisma (DB)

Rules:

  • Controllers never touch db directly
  • Services never touch c (Hono context) or HTTP concerns
  • Validators define the schema — types are inferred from them, never written by hand
  • Response shape always goes through src/lib/response.ts

Response Shape

Every API response follows a consistent envelope. Never return raw data directly.

Success

{
  "success": true,
  "message": "User created",
  "data": { "id": "clx...", "name": "Afif", "email": "[email protected]" }
}

Error

{
  "success": false,
  "message": "User not found"
}

Validation Error (422)

{
  "success": false,
  "message": "Validation error",
  "errors": {
    "email": ["Invalid email"]
  }
}

Available Response Helpers (src/lib/response.ts)

ok(c, data)                    // 200
created(c, data, message?)     // 201
fail(c, message, status?)      // 400 (default)
notFound(c, message?)          // 404
unauthorized(c, message?)      // 401
forbidden(c, message?)         // 403

Scripts

bun dev             # Start dev server with hot reload (--watch)
bun start           # Start production server

bun db:generate     # Generate Prisma client after schema changes
bun db:migrate      # Create and apply a new migration (dev)
bun db:push         # Push schema to DB without migration (prototyping)
bun db:studio       # Open Prisma Studio GUI

Adding a New Resource

Follow this sequence every time — no exceptions, no shortcuts.

# Example: adding a Product resource

1. Validatorsrc/validators/product.validator.ts

import { z } from "zod";

export const createProductSchema = z.object({
  name: z.string().min(1),
  price: z.number().positive(),
  sku: z.string().min(1),
});

export const updateProductSchema = createProductSchema.partial();

export type CreateProductInput = z.infer<typeof createProductSchema>;
export type UpdateProductInput = z.infer<typeof updateProductSchema>;

2. Servicesrc/services/product.service.ts

import { db } from "@lib/db";
import type {
  CreateProductInput,
  UpdateProductInput,
} from "@validators/product.validator";

export const ProductService = {
  getAll: () => db.product.findMany(),
  getById: (id: string) => db.product.findUnique({ where: { id } }),
  create: (data: CreateProductInput) => db.product.create({ data }),
  update: (id: string, data: UpdateProductInput) =>
    db.product.update({ where: { id }, data }),
  remove: (id: string) => db.product.delete({ where: { id } }),
};

3. Controllersrc/controllers/product.controller.ts

import type { Context } from "hono";
import { ProductService } from "@services/product.service";
import { ok, created, notFound } from "@lib/response";
import type {
  CreateProductInput,
  UpdateProductInput,
} from "@validators/product.validator";

export const ProductController = {
  async getAll(c: Context) {
    return ok(c, await ProductService.getAll());
  },
  async getById(c: Context) {
    const product = await ProductService.getById(c.req.param("id"));
    return product ? ok(c, product) : notFound(c, "Product not found");
  },
  async create(c: Context) {
    const body = c.req.valid("json") as CreateProductInput;
    return created(c, await ProductService.create(body), "Product created");
  },
  async update(c: Context) {
    const body = c.req.valid("json") as UpdateProductInput;
    return ok(
      c,
      await ProductService.update(c.req.param("id"), body),
      "Product updated",
    );
  },
  async remove(c: Context) {
    await ProductService.remove(c.req.param("id"));
    return ok(c, null, "Product deleted");
  },
};

4. Routesrc/routes/product.routes.ts

import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { ProductController } from "@controllers/product.controller";
import {
  createProductSchema,
  updateProductSchema,
} from "@validators/product.validator";

export const productRoutes = new Hono();

productRoutes.get("/", ProductController.getAll);
productRoutes.get("/:id", ProductController.getById);
productRoutes.post(
  "/",
  zValidator("json", createProductSchema),
  ProductController.create,
);
productRoutes.patch(
  "/:id",
  zValidator("json", updateProductSchema),
  ProductController.update,
);
productRoutes.delete("/:id", ProductController.remove);

5. Registersrc/routes/index.ts

import { productRoutes } from "@routes/product.routes";

appRoutes.route("/products", productRoutes);

6. Schemaprisma/schema.prisma

model Product {
  id        String   @id @default(cuid())
  name      String
  price     Float
  sku       String   @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
bun db:migrate   # then give the migration a name

Done. New endpoint live at /api/products.


Code Style

Formatted with Prettier. Config in .prettierrc.

# Format all files
bunx prettier --write .

# Check without writing
bunx prettier --check .

Key rules:

| Rule | Value | | --------------- | ------ | | Semicolons | true | | Quotes | Double | | Trailing commas | All | | Print width | 100 | | Tab width | 2 | | Arrow parens | Always | | End of line | lf |


TypeScript Config

Full strict mode — every safety flag is explicitly enabled, not inherited silently from "strict": true.

Key flags beyond the default strict umbrella:

| Flag | Effect | | ------------------------------------ | -------------------------------------------------- | | noUncheckedIndexedAccess | arr[i] returns T \| undefined — must handle | | exactOptionalPropertyTypes | Can't set optional props to undefined explicitly | | noPropertyAccessFromIndexSignature | Must use bracket notation for index signatures | | noImplicitReturns | Every code path must return | | verbatimModuleSyntax | import type enforced — no runtime leaks | | isolatedModules | Each file transpilable independently (Bun req) | | noEmit | Bun handles transpilation — TS is type-check only |

Run type checking:

bunx tsc --noEmit

Production Deployment

Build & Run

Bun runs TypeScript directly — no compile step needed.

NODE_ENV=production bun start

Docker

FROM oven/bun:1 AS base
WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production

COPY . .
RUN bunx prisma generate

EXPOSE 3000
ENV NODE_ENV=production

CMD ["bun", "src/index.ts"]
docker build -t bhaze .
docker run -p 3000:3000 --env-file .env bhaze

Pre-deploy Checklist

  • [ ] DATABASE_URL set in production environment
  • [ ] NODE_ENV=production set
  • [ ] bun db:migrate run against production DB (not db:push)
  • [ ] bunx prisma generate run after deploy if schema changed
  • [ ] Health check endpoint verified: GET /health
  • [ ] No .env file committed to version control

Built with ⚡ by bhaze