Getting started
Create a project, run it with live reload, and add a model, a migration and a page. About fifteen minutes.
Before you start
Go 1.26 or later. Install the anetos tool:
go install anetos.dev/anetos/cli/cmd/anetos@latestSteps
1. Create the project
anetos new blog
cd bloganetos new writes the project (routes, a handler, templ views, sessions
and CSRF protection, migrations, static files with htmx, a test) and a
.env with a fresh APP_KEY, then downloads the dependencies. SQLite is
the default database; --db=postgres or --db=mysql picks another (create
the database and set the DB_* settings in .env before migrating). The
tool reference lists every
file.
2. Run it
go run . migrate # create the database (database/app.db)
go tool anetos dev # http://localhost:8080anetos dev rebuilds and restarts the app when you save a file and
reloads the page in your browser. Edit views/home.templ to see it; its
text is in locales/en/app.yaml, ready for other languages
(Translations).
3. Add a model and its table
go tool anetos make:model Post --migrationThis writes app/models/post.go, its typed columns in
app/models/models_gen.go, and a migration in database/migrations.
Add fields to the model and columns to the migration:
// illustrative
type Post struct {
db.Model
Title string `db:"title" json:"title"`
Body string `db:"body" json:"body"`
}// illustrative
return s.Create("posts", func(t *migrate.Table) {
t.ID()
t.String("title", 200)
t.Text("body")
t.Timestamps()
})Then regenerate the typed columns (anetos dev does it on save) and
create the table:
go tool anetos gen ./app/models # PostCols.Title, PostCols.Body, …
go run . migrateSee Models and Migrations.
4. Add a page
go tool anetos make:handler PostsMake Index list the posts with a view (views/posts.templ), and route
it in routes/web.go:
// illustrative
func (h Posts) Index(c *web.Ctx) error {
posts, err := db.Query[models.Post](c).OrderBy(models.PostCols.CreatedAt.Desc()).Get()
if err != nil {
return err
}
return c.Render(http.StatusOK, views.PostsIndex(posts))
}// illustrative (views/posts.templ)
templ PostsIndex(posts []models.Post) {
@Layout("Posts") {
<h1>Posts</h1>
for _, p := range posts {
<article><h2>{ p.Title }</h2><p>{ p.Body }</p></article>
}
}
}// illustrative
posts := handlers.Posts{}
pages.Get("/posts", posts.Index).Name("posts.index")Pages of posts (Paginate and web.PageURL) are in
Query the database.
Render HTML with templ and Handle HTML forms take it from there.
5. Test and build
go test ./...
go build -o bin/blog .
./bin/blog # runs the app; ./bin/blog help lists the commandsmain_test.go requests the home page with
anetostest, which boots the app with an
in-memory database, like a browser would. Add a test for each page and
form as you go.
The binary embeds the views and static files. Set APP_ENV=production
and the APP_KEY from your secrets in production, and run
./bin/blog migrate before starting the new version.