Skip to content
Quickstart

Quickstart

This walkthrough takes you from an empty directory to a working admin panel with a generated resource — in about five minutes, using SQLite so nothing else needs to be installed.

1. Install the CLI

go install github.com/imfiqhan/steward/cmd/steward@latest

The steward binary only generates code — projects it creates are plain Go programs that run themselves.

2. Create a project

steward new myadmin --module example.com/myadmin --db sqlite
cd myadmin
go mod tidy

This scaffolds a small, readable tree:

myadmin/
├── main.go              # steward.CLI wiring: serve, worker, migrate, …
├── app/app.go           # builds the Admin (DB, config, resource registry)
├── resources/           # one file per resource + registry.go
├── migrations/          # your app's migrations, in Go
└── README.md

3. Migrate and run

go run . migrate up     # creates framework + app tables, seeds defaults
go run . serve          # http://localhost:8080/admin

Sign in with admin / admin.

Warning

The seeded account is for first login only. Change its password immediately — or create your own account with go run . admin:create-user and delete the seeded one.

4. Generate a resource

steward make:resource Task --fields "title:string,notes:text,priority:enum(low,normal,high),done:bool,due_at:datetime:nullable"
go run . migrate up
go run . serve

make:resource writes the model, a migration, and a pre-populated resource file (grid columns, form fields with inferred validation rules, detail view), and registers it in resources/registry.go. The field spec drives sensible defaults — enum becomes a select with a badge column, bool becomes a switch, *_id:fk(...) becomes a searchable BelongsTo.

Already have a schema? Generate from the live database instead:

steward make:resource Product --from-db --dsn "file:app.db" --table products

Or from an existing Go struct:

steward make:resource Invoice --from-struct ./models

5. Make it yours

Open resources/task.go and shape the resource — everything is typed:

tasks := steward.Register[models.Task](app).Title("Tasks").Icon("check")

tasks.Grid(func(g *steward.Grid[models.Task]) {
    g.Column("Title").Sortable().Limit(60)
    g.Column("Priority").Badge(map[any]string{
        "high": "red", "normal": "secondary", "low": "green",
    })
    g.Column("Done").Switch() // inline toggle, saved through validation
    g.QuickSearch("Title", "Notes")
})

tasks.Form(func(f *steward.Form[models.Task]) {
    f.Text("Title").Rules("required|max:255")
    f.Select("Priority").Options(steward.Options{
        "low": "Low", "normal": "Normal", "high": "High",
    })
    f.Datetime("DueAt")
    f.Saving(func(c *steward.Context, m *models.Task) error {
        // runs before persist, with the typed model
        return nil
    })
})

Restart go run . serve and refresh.

Where to go next

  • Grid — columns, filters, actions, tree grids, CSV export
  • Form — all field kinds, rules, hooks, hasMany rows
  • RBAC — roles, permissions, policies, menu visibility
  • Runtime commandsserve, worker, migrate, and deploying panel and background jobs as separate processes