Context
*breeze.Context is what every handler and middleware receives. It carries
the parsed request, the response being built, route params, and a small
per-request key/value store.
router.Handle(breeze.GET, "/users/:id", func(ctx *breeze.Context) error {
id := ctx.Param("id")
ctx.SetHeader("X-Served-By", "breeze")
return ctx.JSON(map[string]string{"id": id})
}) Contexts are pooled and reused between requests, which is central to
Breeze's low-allocation design โ and comes with one rule worth internalizing
early: anything read from ctx.Req must be read before ctx.Next() returns control past it, and nothing about the context should be retained
past the handler returning. Strings on the request point into a pooled
read buffer the next request on that connection reuses.
Writing a response
| Method | Behaviour |
|---|---|
ctx.JSON(data any) error | marshals with go-json, sets Content-Type: application/json |
ctx.HTML(data []byte) error | writes raw bytes with Content-Type: text/html |
ctx.WriteString(s string) error | writes a plain-text body |
ctx.Status(code int) | sets the response status |
ctx.SetHeader(key, value string) | sets a response header |
ctx.GetHeader(key string) string | reads a request header |
A Content-Type set explicitly with SetHeader before a body method
wins over that method's default โ this is what makes application/problem+json possible from ctx.JSON. A previous body
method's content type does not win: ctx.WriteString(...) followed by ctx.JSON(...) sends application/json, not text/plain. Only a type set
through SetHeader is treated as a deliberate choice.
Set response headers before Next(), not after. A middleware that calls ctx.SetHeader after ctx.Next() returns is too late โ the handler has
already written the response. This bit real code once: see the ordering
notes in Middleware Reference.
Params, query, and the store
ctx.Param("id") // route param, e.g. /users/:id
ctx.Query("page") // query string value
ctx.GetParams() // map[string]string, every route param
ctx.Set("user", user) // per-request key/value store
user, ok := ctx.Get("user")
mustUser := ctx.MustGet("user") // panics if absent Binding and validation
type CreateUser struct {
Name string `json:"name" validate:"required,min=2,max=50"`
Email string `json:"email" validate:"required,email"`
ID string `param:"id"`
}
router.Handle(breeze.POST, "/users/:id", func(ctx *breeze.Context) error {
var in CreateUser
if err := ctx.Bind(&in); err != nil {
return nil // the 422 problem+json response is already written
}
return ctx.JSON(in)
}) ctx.Bind decodes the JSON body, query, form, and path params into one
struct and validates it in a single call โ see Request Binding & Validation for the full source
order and rule set.
Chain control
func requireAPIKey(ctx *breeze.Context) error {
if ctx.GetHeader("x-api-key") != wantKey {
ctx.Status(401)
return ctx.JSON(map[string]string{"error": "api key required"})
}
return ctx.Next()
} ctx.Next() runs the next middleware or the handler in the chain and
returns its error. ctx.Abort() stops the chain without running the rest โ
a middleware that doesn't call Next() simply ends the request there.
Errors
A handler returns an error; one function decides what that becomes on the
wire. See Errors as Values.