Getting Started
Install the CLI
$ go install github.com/caspel26/goninja/cmd/goninja@latest
Define a model
Annotate the fields you want exposed with a goninja struct tag —
list, retrieve, create, update, filter — plus an optional
validate tag for input validation:
// models/book.go
type Book struct {
ID string `gorm:"primaryKey;type:uuid" goninja:"list,retrieve"`
Title string `gorm:"size:120;not null" goninja:"list,retrieve,create,update" validate:"required,max=120"`
AuthorID string `goninja:"list,retrieve,create,update,filter"`
Price float64 `goninja:"list,retrieve,create,update,filter" validate:"min=0"`
Published bool `goninja:"list,retrieve,create,update,filter"`
}Generate
$ goninja generate \
-models ./models \
-out ./internal/api \
-package api \
-models-import github.com/you/yourapp/models
Pass -watch to keep it running and regenerate automatically whenever a
.go file under -models changes, debounced so a single save triggers
one regeneration:
$ goninja generate -models ./models -out ./internal/api \
-package api -models-import github.com/you/yourapp/models -watch
This writes typed schemas, handlers, database queries, and an OpenAPI
fragment for the model — plain Go under internal/api, readable and
debuggable, nothing reflected at runtime.
Wire it into a server
// main.go
package main
import (
"net/http"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"github.com/caspel26/goninja"
"github.com/caspel26/goninja/docsui"
"myapp/internal/api"
"myapp/models"
)
func main() {
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
db.AutoMigrate(&models.Author{}, &models.Book{}) // goninja doesn't generate migrations
mux := http.NewServeMux()
app := goninja.NewAPI("Bookstore API", "0.1.0")
app.Mount(mux,
api.NewAuthorResource(db),
api.NewBookResource(db),
)
app.MountDocs(mux, "/docs", docsui.SwaggerUI())
http.ListenAndServe(":8080", mux)
}That’s a full net/http server: GET/POST /books, GET/PUT/DELETE /books/{id}, filtering, pagination, validation, and /docs — all from
the struct at the top. goninja.NewAPI is the app’s entry point;
api.Mount just does Register(mux) plus merges each resource’s OpenAPI
fragment for every resource passed to it, and api.MountDocs serves a
rendered UI over the result — both are thin wrappers over the standalone
openapi/docsui packages, so you’re never required to go through them
either.
Next: Extending a Resource to add your own logic without touching generated files.