Production-ready Bun + Hono + TypeScript + PostgreSQL + Drizzle ORM monorepo with built-in observability.
- Monorepo: Turborepo with Bun workspaces
- API Framework: Hono with OpenAPI/Scalar docs
- Database: PostgreSQL with Drizzle ORM
- Auth: OAuth 2.0 (Google, extensible)
- Observability: Prometheus + Grafana + Loki
- Security: JWT, encryption, rate limiting, CSRF protection
- Type Safety: Full TypeScript with Zod validation
# Clone and install
git clone <repo-url> my-project
cd my-project
bun install
# Configure environment
cp apps/api/.env.example apps/api/.env
# Edit .env with your values
# Start services (PostgreSQL + Prometheus + Grafana + Loki)
docker compose -f docker-compose.dev.yml up -d
# Run migrations
bun run db:migrate
# Start development
bun run dev| Service | URL | Credentials |
|---|---|---|
| API | http://localhost:8000 | - |
| API Docs | http://localhost:8000/docs | - |
| Grafana | http://localhost:8001 | admin / admin |
| Prometheus | http://localhost:9090 | - |
| Loki | http://localhost:3100 | - |
├── apps/
│ └── api/ # Main API service
│ ├── src/
│ │ ├── modules/ # Feature modules
│ │ │ └── auth/ # Authentication (OAuth)
│ │ └── index.ts # Entry point
│ └── drizzle/ # Migrations
│
├── packages/
│ ├── config/ # Environment & constants
│ ├── db/ # Database schemas & services
│ └── shared/ # Utilities, logging, middleware
│
├── infra/
│ └── monitoring/ # Prometheus, Grafana, Loki configs
│
├── docs/ # Documentation
│ ├── ARCHITECTURE.md # System design
│ ├── AUTHENTICATION.md # OAuth setup
│ ├── DATABASE.md # Schema & queries
│ ├── LOGGING.md # Structured logging
│ └── MONITORING.md # Metrics & observability
│
└── docker-compose.dev.yml # Local development services
# Development
bun run dev # Start all services
bun run dev --filter=@repo/api # Start API only
# Database
bun run db:generate # Generate migrations
bun run db:migrate # Apply migrations
# Docker
docker compose -f docker-compose.dev.yml up -d # Start services
docker compose -f docker-compose.dev.yml down # Stop services
docker compose -f docker-compose.dev.yml logs -f # View logs
# Quality
bun run typecheck # Type checking
bun run lint # Lintingmkdir -p apps/api/src/modules/myfeature/{handlers,services}// apps/api/src/modules/myfeature/handlers/get-items.handler.ts
import { createRoute, z } from "@hono/zod-openapi";
import type { RouteHandler } from "@hono/zod-openapi";
import { StatusCodes, errorResponseSchemas } from "@repo/config";
export const getItemsRoute = createRoute({
method: "get",
path: "/items",
tags: ["Items"],
responses: {
[StatusCodes.HTTP_200_OK]: {
content: { "application/json": { schema: z.object({ items: z.array(z.any()) }) } },
description: "Success",
},
...errorResponseSchemas,
},
});
export const getItemsHandler: RouteHandler<typeof getItemsRoute> = async (c) => {
return c.json({ items: [] }, StatusCodes.HTTP_200_OK);
};// packages/db/src/schema/items/items.db.ts
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core";
export const itemsTable = pgTable("items", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
deletedAt: timestamp("deleted_at", { withTimezone: true }),
});bun run db:generate && bun run db:migrate# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/backend-template
# Server
PORT=8000
NODE_ENV=development
API_URL=http://localhost:8000
CORS_ORIGIN=http://localhost:3000
# Security
JWT_SECRET=your-jwt-secret-min-32-chars
ENCRYPTION_KEY=your-64-char-hex-encryption-key
# OAuth - Google
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
# Observability
LOG_LEVEL=info
LOKI_HOST=http://localhost:3100Generate secrets:
# JWT Secret
openssl rand -base64 32
# Encryption Key
openssl rand -hex 32// Route + handler colocated with OpenAPI schema
export const myRoute = createRoute({
/* OpenAPI spec */
});
export const myHandler: RouteHandler<typeof myRoute> = async (c) => {
/* impl */
};// Namespace with optional logger and transaction support
export namespace UsersService {
export async function create(
payload: NewUser,
logger?: Logger,
options?: { tx?: DBTransaction },
) {
/* impl */
}
}import { logger } from "@repo/shared";
logger.info("Operation completed", {
module: "auth", // Required: db | auth | users | system | session | security | http
action: "oauth:callback", // Required: context:operation
userId: user.id, // Additional context
});| Document | Description |
|---|---|
| ARCHITECTURE.md | System design & patterns |
| AUTHENTICATION.md | OAuth flow & adding providers |
| DATABASE.md | Schemas, migrations, queries |
| LOGGING.md | Structured logging guide |
| MONITORING.md | Metrics, Grafana, Loki setup |
| Category | Technology |
|---|---|
| Runtime | Bun |
| Framework | Hono |
| Database | PostgreSQL + Drizzle |
| Validation | Zod |
| Documentation | Scalar |
| Logging | Pino + Loki |
| Metrics | Prometheus + Grafana |
| Monorepo | Turborepo |
lsof -ti:8000 | xargs kill -9docker compose -f docker-compose.dev.yml ps
docker compose -f docker-compose.dev.yml restart postgresrm -rf node_modules apps/*/node_modules packages/*/node_modules
bun install- Check Prometheus targets: http://localhost:9090/targets
- Verify API metrics: http://localhost:8000/metrics
- Check time range in Grafana (last 5-15 minutes)
Happy Building! 🚀