Part 1: Setup & First View¶
In this first part you'll create a Go module, set up a minimal Burrow server, and serve a "Hello, Polls!" homepage.
Source code: tutorial/step01/
Create the Project¶
After writing the code below, run go mod tidy to fetch all transitive dependencies before building.
Write the Server¶
Create main.go:
package main
import (
"context"
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
"github.com/oliverandrich/burrow"
_ "github.com/oliverandrich/den/backend/sqlite" // register sqlite:// scheme
"github.com/urfave/cli/v3"
)
func main() {
srv := burrow.NewServer(
&homepageApp{},
)
cmd := &cli.Command{
Name: "polls",
Usage: "Polls tutorial application",
Version: "0.1.0",
Flags: srv.Flags(nil),
Action: srv.Run,
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}
The Homepage App¶
Add the following code to the same main.go file, below the main() function.
Every Burrow application is composed of apps. Each app implements the burrow.App interface — a Name() that returns a unique identifier. Our homepage app also implements HasRoutes to register an HTTP endpoint:
type homepageApp struct{}
func (a *homepageApp) Name() string { return "homepage" }
func (a *homepageApp) Routes(r chi.Router) {
r.Get("/", burrow.Handle(func(w http.ResponseWriter, r *http.Request) error {
return burrow.Text(w, http.StatusOK, "Hello, Polls!")
}))
}
A few things to note:
- The blank import
_ "github.com/oliverandrich/den/backend/sqlite"registers the SQLite backend with Den sosqlite://DSNs work. Every binary that opens a database must blank-import the backend that matches its DSN — Burrow doesn't pull either one in by default so production binaries only link the engine they actually use. burrow.HandlerFunchas the signaturefunc(w http.ResponseWriter, r *http.Request) error— just like the standard library, but with an error return. This lets you propagate errors instead of handling them in every handler.burrow.Handle()wraps aHandlerFuncinto a standardhttp.HandlerFunc. If the handler returns an*HTTPError, it sends the appropriate status code and message.burrow.Text()is a helper that writes a plain text response.
Run It¶
You should see log output indicating the server has started. Burrow creates an app.db SQLite database in the working directory. Open http://localhost:8080 in your browser — you'll see "Hello, Polls!".
What Happens at Boot¶
When srv.Run is called, Burrow follows roughly this sequence:
- Parse configuration from CLI flags, environment variables, and TOML files
- Open the database via Den (SQLite with WAL mode, or PostgreSQL)
- Register documents from all
HasDocumentsapps - Call
Configure()on eachConfigurableapp with the sharedAppConfig - Build the global template set from all
HasTemplatesapps - Set up the Chi router with core middleware
- Apply middleware from all
HasMiddlewareapps - Register routes from all
HasRoutesapps - Start the HTTP server with graceful shutdown
This is the short version. The full ordering — i18n bundle creation, storage opening, translation loading, the second PostConfigure pass, seeding, and background-process startup — is documented in Boot Sequence. You won't need that level of detail until later in the tutorial.
Built-in CLI Flags¶
Because Burrow uses urfave/cli, your server gets flags for free:
Common flags include --host, --port, --database-dsn, and --log-level.
Next¶
In Part 2, you'll add a database, define models for questions and choices, and write your first migration.