|
|
||
|---|---|---|
| scripts | ||
| src | ||
| template | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
Introduction
A template for Bun HTTP servers built with Hono. Provides an opinionated HTTP middleware pipeline, structured request logging, OpenAPI documentation with Scalar UI, and a Bun server host with graceful shutdown. Designed to be used standalone or as the backend in a monorepo alongside one or more @temir.ra/create-hono-spa SPA packages mounted as route groups.
Table of Contents
Quick Start
bun create caches the template package - a newer published version will not be picked up automatically. Pin the version or clear the cache to use the latest.
# placeholder:
# <NEW_PACKAGE: <NEW_PACKAGE>
# <@_VERSION: <@_VERSION>
# identify the latest version of the template package as <@_VERSION.
bun info "@temir.ra/create-hono-server" version
# create a new library from the template version
bun create --no-install --no-git "@temir.ra/hono-server<@_VERSION>" <NEW_PACKAGE>
# or
# clear package manager cache to ensure the latest template version is used
bun pm cache rm
# create a new library from the latest template version
bun create --no-install --no-git "@temir.ra/hono-server" <NEW_PACKAGE>
# dependencies must be installed manually
cd <NEW_PACKAGE>
bun install
AI Assistant Context
To generate an AI coding assistant context file for this project:
Generate an AI coding assistant context file. Analyze the project structure and source files, using this README as the primary reference for architecture and conventions. Give particular attention to: the three-layer architecture (main → createAppHost → startServerHost) and the separation of concerns between app host and server host, AppEnv and how it is extended to carry custom variables through the Hono context, the middleware pipeline and the scoped logging system, and endpointGroups as the primary extension and integration point for sub-apps.
Documentation
The following sections explain the configurations and conventions baked into the generated package.
template/
The files in template/ were created from the @temir.ra/ts-lib@0.5.0 template and then updated to fit the needs of a Hono server template.
bun create --no-install --no-git --force "@temir.ra/ts-lib@0.5.0" "template/"
Official documentation provided by the Hono team is a great resource.
Architecture
The generated server is organized around three roles:
main.ts → createAppHost() → startServerHost()
main.ts is the entry point and orchestrator. It calls createAppHost() to configure the Hono application, then passes the result to startServerHost(), which hands it to Bun.serve() and manages the process lifecycle.
- App host (
app-host.ts) - owns the application: what middleware runs, which routes and endpoint groups are mounted, what the API looks like. - Server host (
server-host.ts) - owns the server: which port it listens on, how it shuts down on process signals. main.ts- wires them together; the natural place to compose the application.
// main.ts
const appHost = createAppHost<AppEnv>({
basePath: 'api/',
endpointGroups: [myRoutes],
});
startServerHost({ appHost });
src/app-host.ts
createAppHost(options) constructs and returns a configured Hono<AppEnv> instance.
Built-in middleware pipeline (applied in this order):
| Middleware | Purpose |
|---|---|
Logging |
Attaches a scoped, leveled log function factory to c.var.getLogFunction |
requestId |
Attaches a unique request ID to c.var.requestId |
RequestLogger |
Logs method and path at TRACE level using the request scope |
compress |
Gzip/Brotli response compression |
secureHeaders |
Adds security response headers |
Built-in endpoints:
| Path | Description |
|---|---|
/health |
Returns ok with status 200 |
/buildinfo |
Returns the contents of buildinfo.txt |
/<openApiPath> |
OpenAPI JSON spec (default path: openapi) |
/<openApiUiPath> |
Scalar API reference UI (default path: scalar) |
Options:
type AppHostOptions<E extends Env> = {
basePath?: string; // base path prefix for all routes
middleware?: MiddlewareHandler<E>[]; // additional middleware, applied after built-ins
endpointGroups?: Hono<E>[]; // Hono sub-apps mounted at '/'
openApi?: {
path?: string; // OpenAPI spec path (default: 'openapi')
uiPath?: string; // Scalar UI path (default: 'scalar')
title?: string;
description?: string;
};
}
AppEnv
export interface AppEnv extends Env {
Variables: LoggingVariables
}
Extends Hono's Env with LoggingVariables, making c.var.getLogFunction and c.var.requestId available throughout. Exported to be extended when additional context variables are needed:
interface MyEnv extends AppEnv {
Variables: AppEnv['Variables'] & {
user: User;
}
}
const appHost = createAppHost<MyEnv>({ ... });
const myRoutes = new Hono<MyEnv>();
myRoutes.get('/profile', (c) => c.json(c.var.user));
Adding endpoints
const myRoutes = new Hono<AppEnv>();
myRoutes.get('/items', (c) => c.json({ items: [] }));
const appHost = createAppHost<AppEnv>({
endpointGroups: [myRoutes],
});
Mounting a SPA
Pass a createSpa() result directly as an endpoint group. See @temir.ra/create-hono-spa for details.
import { createSpa } from '@scope/my-spa/spa';
const appHost = createAppHost<AppEnv>({
basePath: 'api/',
endpointGroups: [createSpa({ basePath: 'api' })],
});
src/server-host.ts
startServerHost(options) wraps Bun.serve() and registers SIGINT/SIGTERM handlers for graceful shutdown.
type ServerHostOptions = {
port?: string | number; // default: 7200
appHost: Hono<AppEnv>;
stopCallback?: (server: Bun.Server<undefined>) => void;
}
On SIGINT or SIGTERM: calls stopCallback (if provided), stops the Bun server, then exits with code 0.
src/middleware/logging/
Logging(options)
Attaches a getLogFunction(scope?) factory to the request context as c.var.getLogFunction. Call it in any handler or middleware to get a scoped log function:
const log = c.var.getLogFunction('my-scope');
log(LogLevel.INFO, 'Processing request', { id });
Log output format: [<ISO timestamp>] [<requestId>] [<scope>] [<LEVEL>] <message>
Options:
type LoggingOptions = {
logLevel: LogLevel; // global default level
logLevelPerScope?: Record<string, LogLevel>; // per-scope overrides
}
LogLevel enum: NONE | ERROR | WARNING | INFO | TRACE
The template configures WARNING globally and TRACE for the request scope.
RequestLogger()
Logs <METHOD> <path>[?<query>] at TRACE level using the request scope. Applied after requestId and Logging so all three are available.
DevOps
# remove dist/ and tsconfig.build.tsbuildinfo
bun run clean
# remove dist/ only
bun run clean:dist
# remove tsconfig.build.tsbuildinfo only
bun run clean:tsbuildinfo
# compile + bundle
bun run build
# create a new test hono server in example/
bun run dist/cli.bundle.js -- example
Publish - see Publish.
Change Management
- Create a new branch for the change.
- Make the changes and commit.
- Bump the version in
package.json. - Add an entry for the new version in
CHANGELOG.md. - Pull request the branch.
- After merge, run
bun run build- ensures artifacts are current before publish. - Publish.
Publish
See the following sources to configure the target registry and authentication.
⚠️ Package Scope and the authentication for the target registry must be aligned.
npmjs.org
Publish to the public npm registry.
# authenticate
npm login
# publish
bun publish --registry https://registry.npmjs.org/ --access public
Custom registry
# placeholder:
# <SCOPE_WITHOUT_AT: <SCOPE_WITHOUT_AT>
# <REGISTRY_URL: <REGISTRY_URL>
# <BUN_PUBLISH_AUTH_TOKEN: <BUN_PUBLISH_AUTH_TOKEN>
~/.bunfig.toml or bunfig.toml:
[install.scopes]
"<SCOPE_WITHOUT_AT>" = { url = "<REGISTRY_URL>", token = "$BUN_PUBLISH_AUTH_TOKEN" }
# authenticate
$env:BUN_PUBLISH_AUTH_TOKEN = "<BUN_PUBLISH_AUTH_TOKEN>"
# publish
bun publish