App setup
Brick[TCtx], Config, middleware, handler, and docs endpoints.
Source: brick.go.
Brick[TCtx]
Brick[TCtx] owns three things: a bunrouter router, a huma.API (OpenAPI 3.1), and a db.DB wrapper. TCtx is your app-defined context type — use Config[any] when you don't need one.
type Config[TCtx any] struct {
Title string
Version string
DB *bun.DB
API huma.API // optional: inject your own (tests)
Resources []ResourceConfig
Routes func(api huma.API, db *db.DB)
CtxFromRequest func(req *http.Request, base *AppCtx) *TCtx
}DB == nilis an error, never a panic.- When
APIis nil, Brick builds its own router + adapter and owns thehttp.Handler. WhenAPIis supplied (typicallyhumatestin tests), Brick uses it as-is andHandler()/Router()return nil — the caller owns the router. Routesis the only custom-route mechanism; app code cannot override a resource's CRUD routes.
Lifecycle
b, err := brick.New(cfg) // registers Resources + Routes
b.Huma() // underlying huma.API
b.DB() // *db.DB wrapper
b.Router() // *bunrouter.Router (nil when API injected)
b.Handler() // middleware + router (nil when API injected)
b.ListenAndServe(":8080") // errors when Brick doesn't own the routerMiddleware and identity
b.Middleware() stores your typed app context on every request. Brick mounts no auth of its own — it calls CtxFromRequest(r, nil) with a nil base, and stores the result only if it implements AppCtxer. Nil result means anonymous; guards treat it as unauthenticated. Header parsing and crypto live in your middleware, Brick owns the plumbing. See Context.
Handler() wraps the router with requestIDMiddleware + Middleware(): X-Request-ID is passed through or minted, echoed as a response header, and put in the request context for error responses and logs.
OpenAPI and Scalar
Huma serves the spec itself: GET /openapi.json (same bytes as
b.OpenAPISpec()), plus Stoplight docs at /docs — no wiring needed. For
the Scalar viewer, mount /reference with an absolute spec URL built
per request: scalar-go rejects relative URLs, and the public host is only
known at request time (localhost vs your Vercel domain):
b.Router().GET("/reference", func(w http.ResponseWriter, req bunrouter.Request) error {
scheme := req.Header.Get("X-Forwarded-Proto") // edge scheme behind proxies
if scheme == "" {
scheme = "http"
if req.TLS != nil {
scheme = "https"
}
}
html, err := brick.ScalarDocsHTML(scheme + "://" + req.Host + "/openapi.json")
if err != nil {
http.Error(w, "reference unavailable", http.StatusInternalServerError)
return nil
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write([]byte(html))
return nil
})See Deploy to Vercel for the full serverless pattern.