v0.6.0 · MIT
Lead with a struct.
tanGO does the footwork.
tanGO is a small, explicit web framework for Go. Plain structs and interfaces, named routes, typed migrations and a server-rendered admin. Scaffolding writes plain Go you own, and there's no hidden configuration.
$ go get github.com/angvp/tango
Hello, tanGO
A project that runs, and an app you register by hand.
Apps plug in through public contracts. Scaffolding writes plain Go you own. Nothing is generated at runtime, and boot never scans your filesystem.
func (App) Register(registry *tango.Registry) error {
return registry.Routes().Include("/greetings/", tango.URLs{
tango.Path("GET", "/{name}/", hello, tango.Name("hello")),
})
}
func hello(ctx *tango.Context) error {
name := ctx.Param("name")
return ctx.JSON(200, map[string]string{
"message": "Hello, " + name + "!",
})
}
// main.go: installing the app is one line you write
config.InstalledApps = []tango.App{
admin.New(store),
greetings.App{},
}
$ tango newproject shop
$ cd shop
$ go run .
listening on :8000
# open http://localhost:8000/admin/ in a browser
Where do I start?
Pick what you're building. Each one has a runnable example.
Or follow the step-by-step tutorial, which builds one app from an empty directory to a deployment.
-
A JSON API
The smallest runnable app: one route, no database.
-
A server-rendered app with an admin
A JSON API and an HTML admin sharing models, with foreign keys.
-
Realtime
WebSocket rooms with graceful shutdown.
What you get
Batteries included, and every one of them is visible.
-
Explicit lifecycle
Config,App,Registry. Nothing boots implicitly, and a graceful shutdown you wire in two lines ofmain. -
Named routes
A URL dispatcher with reverse lookup. It's built on chi, but chi never shows up in your imports.
-
Plain-struct models
A handful of tags:
tango:"pk",unique,index,fk=Other. That's the whole grammar. -
SQLite & PostgreSQL
A minimal
db.Storefor CRUD and raw SQL, with foreign keys, validation and cascade delete. OneTANGO_DB_DSNpicks the database. -
Typed Go migrations
makemigrations,migrate,migrate down. Migrations are generated Go, not a separate schema DSL. -
HTML admin
List, create, edit, delete, search and sort behind a session login, with CSRF protection and rate-limited logins.
-
Auth, with reset and verification
An optional
accountsapp for cookie sessions, password reset and email verification, or stateless HS256 JWTs throughauth/jwt. -
Realtime rooms
A WebSocket
Hubwith rooms, reconnect windows and backpressure, wired into graceful shutdown. -
Jobs & observability
Background jobs and scheduling, structured HTTP logging, request IDs, and a shared metrics recorder.
-
Production defaults
Scaffolded projects read
PORT, cap request bodies, time out slow headers and log unmatched requests.
If writing normal Go is simpler than invoking tanGO, write normal Go.
The rule of thumb behind every design decision
- Prefer Go primitives before replacing them.
- Prefer explicit structure over convention magic.
- Treat JSON and API-only apps as first-class. HTML is optional.
- The admin is a normal app, not a privileged subsystem.
- No custom template language.
- Third-party internals stay behind tanGO's abstractions.
The CLI
It wraps go build. It doesn't replace it.
Examples
Built and validated in CI, with behaviour tests where the workflow needs them.
- accounts-mailPassword reset and email verification, with emails printed to the terminal
- api-with-adminA JSON API and HTML admin sharing models, with foreign keys
- boardThe tutorial's app, tested end to end on SQLite and PostgreSQL
- cacheA bounded in-process cache for a recomputable read
- jsonapiThe smallest runnable JSON-only app
- jwt-apiStateless bearer-token authentication
- notes-starterA starter app you can keep and grow
- realtime-chatWebSocket rooms with graceful shutdown
- reusable-greetingsA reusable app shipping its own migrations and static assets
- reusable-greetings-hostA host project installing the reusable greetings app
- spa-accountsThe backend of a single-page app: accounts' JSON mode with bearer tokens
- uploadsA document locker: one multipart upload, served back to its owner
Dogfooding
This website is a tanGO app.
Named routes, html/template views and embedded static assets. The docs and examples
you browse here are rendered, when the site starts, from the tanGO release it's built against, so they
always match that release's code.
routes.Include("/docs/", tango.URLs{
tango.Path("GET", "/", a.docsIndex, tango.Name("index")),
tango.Path("GET", "/{slug}/", a.rootDoc, tango.Name("page")),
tango.Path("GET", "/{section}/{slug}/", a.sectionDoc, tango.Name("doc")),
})
// in a template: {{url "docs:page" "slug" "limitations"}}
Know before you choose
- It is v0.x. tanGO is at v0.6.0. Patch releases never break its public API; before v1.0, a minor release may, but only after a deprecation window (see versioning and compatibility).
- State is single-process and in-memory. Realtime rooms, rate limiting and the mail outbox do not coordinate across server instances. What that means for more than one instance.
- The admin is for trusted internal use, not a public-facing panel. The ORM is minimal foundations, not a full ORM.
The limitations list the rest. Read them before you depend on tanGO for anything beyond experimentation.