https://github.com/hpbyte/hono-starter
๐ Enterprise-ready Hono starter with NestJS-style architecture, dependency injection, and PostgreSQL integration - built for Bun runtime
https://github.com/hpbyte/hono-starter
boilerplate bunjs dependency-injection drizzle-orm honojs http-server nestjs postgresql typescript
Last synced: about 2 months ago
JSON representation
๐ Enterprise-ready Hono starter with NestJS-style architecture, dependency injection, and PostgreSQL integration - built for Bun runtime
- Host: GitHub
- URL: https://github.com/hpbyte/hono-starter
- Owner: hpbyte
- Created: 2025-09-20T04:28:51.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2026-01-29T01:49:25.000Z (4 months ago)
- Last Synced: 2026-01-29T17:19:28.697Z (4 months ago)
- Topics: boilerplate, bunjs, dependency-injection, drizzle-orm, honojs, http-server, nestjs, postgresql, typescript
- Language: TypeScript
- Homepage:
- Size: 89.8 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Hono Starter
A starter kit for building Bun + Hono services with a [NestJS](https://nestjs.com/)-like structure, Dependency injection is baked in, and [PostgreSQL](https://www.postgresql.org/) via [Drizzle ORM](https://orm.drizzle.team/).
## Why?
Hono is really great but due to its unopinionated nature, there's a lack of guidance in terms of application structure and best practices for enterprise applications.
This aims to provide as a starting point for enterprise apps and contains ready-to-use base tooling so you can focus on features instead of wiring from scratch.
## Stack
- โก **Bun + Hono** for fast server-side rendering and routing
- ๐ **Inversify DI** with Nest-like modules, controllers, and injectable services
- ๐๏ธ **Drizzle ORM** already configured for PostgreSQL
- ๐ **Background runtime** that auto-discovers jobs and handles lifecycle
- ๐ก๏ธ **Zod** for schema validation and type-safe configuration
- ๐งฑ **Layered structure** (`core`, `modules`, `shared`) for better maintainability
- ๐งช **TypeScript-first** with ESLint, Prettier, and `tsc` ready to run
## Project Structure
```
src/
โโโ bootstrap.ts # Bootstraps the DI container, server, and runtime
โโโ core/ # Foundation services used across the app
โ โโโ config/ # Typed config loading & validation (Zod)
โ โโโ database/ # Drizzle client, config, migrations output
โ โโโ di/ # Module decorator(s) + container factory
โ โโโ http/ # Hono server wrapper with lifecycle helpers
โโโ modules/ # Feature modules (controllers, services, jobs)
โ โโโ background/ # Sample background job + runtime coordination
โ โโโ health/ # `/health` endpoint and status checks
โ โโโ user/ # `/users` example using Drizzle query API
โโโ shared/ # Reusable interfaces and type contracts
```
## Getting Started
### Prerequisites
- [Bun](https://bun.sh) `>=1.2`
- Docker & Docker Compose (for local PostgreSQL)
### Installation
```bash
bun install
cp .env.example .env
```
Update `.env` (see [Environment Variables](#environment-variables)).
### Run the stack locally
```bash
docker compose up -d db # start postgres
bun run db:migrate # apply pending migrations
bun run dev # start Hono with hot reload on http://localhost:3000
```
### Running with Docker
```bash
docker compose up --build -d
```
## Architecture Notes
- **Dependency Injection**: `@Module({...})` ties together providers, controllers, and background jobs. `bootstrap.ts` collects modules, builds an Inversify container, and resolves the `HttpServer` and `BackgroundRuntime` singletons.
- **HTTP Controllers**: Anything bound as a controller is auto-registered on the Hono app when the server starts. Implement `HttpController.registerRoutes(app)` to declare routes.
- **Background Jobs**: Implement `IBackgroundJob` (`id`, `start`, `stop`) and add the job class to a module's `backgroundJobs` array. The runtime starts every job on boot and stops them on shutdown.
- **Graceful Shutdown**: `src/main.ts` listens for `SIGINT/SIGTERM`, stops jobs, and closes the HTTP server in order.
- **Database Access**: Use the injected `DrizzleClient` (`drizzle.database`) for type-safe queries. Place schema files under `src/core/database/schema/` for Drizzle Kit to pick them up.
## Environment Variables
`src/core/config/config.schema.ts` validates configuration at startup. Provide these variables via `.env` or your environment:
| Variable | Required | Default | Description |
| -------------- | -------- | ------- | ---------------------------- |
| `DATABASE_URL` | โ
| โ | PostgreSQL connection string |
| `PORT` | โ | `3000` | HTTP server port |
Example `.env`:
```env
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/hono
PORT=3000
```
## Database Workflow
Drizzle Kit is preconfigured via `src/core/database/drizzle.config.ts`.
```bash
bun run db:generate # generate SQL migration files from schema changes
bun run db:migrate # apply migrations
bun run db:push # push schema directly (not recommended for prod)
bun run db:studio # open Drizzle Studio UI
```
Place table definitions in `src/core/database/schema/` (e.g. `users.ts`). They are aggregated automatically when you run Drizzle commands.
## Adding New Features
### Create a module
1. Create a directory under `src/modules/`.
2. Add services/controllers with the `@Injectable()` decorator.
3. Register them in `.module.ts` using `@Module({ providers, controllers, backgroundJobs })`.
4. Export the module class and register it inside the `modules` array in `src/bootstrap.ts`.
### Register an HTTP controller
```ts
@Injectable()
export class ExampleController implements HttpController {
constructor(private readonly svc: ExampleService) {}
registerRoutes(app: Hono) {
app.get('/example', () => this.svc.handle())
}
}
```
Once the module is included in `bootstrap.ts`, the controller routes are attached automatically.
### Add a background job
```ts
@Injectable()
export class ReportJob implements IBackgroundJob {
readonly id = 'report-job'
constructor(private readonly handler: ReportHandler) {}
async start() {
await this.handler.run()
}
async stop() {
await this.handler.stop()
}
}
```
Add `ReportJob` to the module's `backgroundJobs` array to have it resolved and started.