Ohm is a low resistance, Go web framework for building server-rendered web apps with clear defaults and ordinary Go code.
It gives you the shape of a complete web app without hiding Go:
- A framework CLI for creating apps and generating code.
- An app-owned CLI for serving, routing, migrating, seeding, and replaying.
- Routing behind a small Ohm handler layer.
html/templateviews for pages, partials, layouts, components, forms, and errors.- Go-native form validations with structured field errors.
- A blessed htmx adapter for target-aware server-rendered fragments.
- Typed config backed by deterministic
.envloading. sqlcqueries andgoosemigrations instead of an ORM.- Structured
sloglogging with sensitive-value scrubbing and trace correlation. - Low-pollution OpenTelemetry tracing at framework seams, with observed spans for slow or failing helper work.
- Replayable request snapshots that can become regression tests.
Ohm is for people who want the productive parts of a full-stack framework while keeping ownership of ordinary Go code.
Install the latest released CLI:
go install github.com/mgomes/ohm/cmd/ohm@latestFrom this checkout, install the local CLI:
go install ./cmd/ohmCheck the installed version:
ohm versionUse SQLite for a local app that doesn't need Postgres:
ohm new journal --db sqlite --module example.com/journal
cd journal
cp .env.development.example .env.development
cp .env.test.example .env.test
just check
just serverPostgres is the default production path:
ohm new journal --db postgres --module example.com/journalSet DATABASE_URL in .env.development and .env.test, then run the same
checks.
ohm generate handler Posts
ohm generate migration create_posts
ohm generate resource Posts title:string body:textThe app owns generated code. Keep it readable, testable, and changeable.
Each generated app has its own binary. The framework CLI is not required at runtime.
go run ./cmd/journal server
go run ./cmd/journal routes
go run ./cmd/journal migrate up
go run ./cmd/journal migrate status
go run ./cmd/journal db seed
go run ./cmd/journal replay ./tmp/replays/request.json