Skip to content

Your First Route

A Zinc handler receives a request-scoped *zinc.Context and returns an error:

func(c *zinc.Context) error

Routes and middleware share that shape. Route registration itself stays terse:

app.Get("/users/{id}", showUser)
app.Get("/teams/{teamID}/users/{userID}", func(c *zinc.Context) error {
return c.JSON(zinc.Map{
"team_id": c.Param("teamID"),
"user_id": c.Param("userID"),
"verbose": c.QueryOr("verbose", "false"),
})
})

Route params come from {name} segments. Query values come from the request URL.

type CreateUserInput struct {
TeamID int `path:"teamID"`
Name string `json:"name"`
Email string `json:"email"`
}
app.Post("/teams/{teamID}/users", func(c *zinc.Context) error {
var input CreateUserInput
if err := c.Bind().All(&input); err != nil {
return err
}
return c.Status(zinc.StatusCreated).JSON(input)
})

Bind().All reads route params, query values, body data, and validation when a validator is configured.

app.Get("/users/{id}", func(c *zinc.Context) error {
user, err := findUser(c.Param("id"))
if err != nil {
return zinc.ErrNotFound.WithMessage("user not found")
}
return c.JSON(user)
})

Handlers return errors so Zinc can apply one consistent error policy. Invalid source-defined route declarations panic during startup; you do not check an error after every app.Get or app.Post call.

  • Routing covers patterns, groups, and named routes.
  • Binding covers the complete typed binding API.
  • Errors covers HTTP errors and custom error handlers.