Introduction
ezz is a Go framework for JSON APIs. You write a function that takes a typed request struct and returns a typed response, register it on a route, and ezz takes care of the parts that are the same in every service: reading values out of the request, validating them, rejecting unauthenticated callers, shaping errors, and describing the endpoint in an OpenAPI document.
It is a thin layer, not a platform. Routing is Go's own net/http.ServeMux. Middleware is func(http.Handler) http.Handler. An *ezz.App is an http.Handler, and every handler you write is one too, so you can mix ezz code with plain net/http code in the same binary.
What ezz does for you
- Binds requests. Struct tags map path segments, query parameters, headers, form fields, and the JSON body onto your request type.
- Validates input. A
validatetag rejects bad input with a400before your function runs. - Authenticates and authorizes. Register an auth provider and routes require a valid token unless you mark them public. Role and permission rules live on the route.
- Resolves authorization from your data. Roles and permissions come from a resolver you implement against your own tables, not from a token claim.
- Documents itself. The OpenAPI 3 spec is generated from your handler types and served next to a Scalar UI.
- Fronts GoTrue. If you use GoTrue or Supabase Auth, ezz can proxy it so the auth server never faces the internet.
What a handler looks like
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
}
app.POST("/users", ezz.H(func(ctx *ezz.Context, req CreateUserRequest) (*User, error) {
if store.EmailTaken(req.Email) {
return nil, ezz.Conflict("email already registered")
}
return store.Create(req.Name, req.Email), nil
}))
By the time your function runs, the body has been decoded, Name and Email have been checked, and the caller has been authenticated. Returning an error writes a JSON error body with a matching status code. Returning a value writes it as JSON with 200.
When to reach for it
ezz fits services that speak JSON over HTTP and want types and documentation to stay in sync: internal APIs, mobile and SPA backends, and anything sitting in front of GoTrue. It is a good fit when you like the standard library and want a little structure on top of it.
It is not a batteries-included web framework. There is no ORM, no template engine, no asset pipeline, and no background job runner. If you need server-rendered HTML or websockets as a first-class concern, use ezz for the JSON parts and plain net/http for the rest.
Requirements
- Go 1.22 or newer, for method-aware
ServeMuxpatterns andRequest.PathValue. The module itself is built with the Go 1.25 toolchain. - No third-party dependencies for the core packages (
ezz,ezz/binding,ezz/middleware,ezz/openapi,ezz/auth). The optionalezz/auth/ratelimitpackage brings in a Redis client.