httpx

package
v0.17.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package httpx is the routing layer.

It is a thin shell over net/http: Middleware is the standard func(http.Handler) http.Handler, so every middleware written for the Go ecosystem works here unchanged.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Chain

func Chain(h http.Handler, mws ...Middleware) http.Handler

Chain composes middlewares. The first in the list is the outermost.

Types

type Context added in v0.11.0

type Context struct {
	// Response and Request are exported: a handler that needs the standard
	// library reaches for it directly instead of waiting for a wrapper.
	Response http.ResponseWriter
	Request  *http.Request
	// contains filtered or unexported fields
}

Context is what a controller action receives.

It is everything a controller action gets, and no more: the request, the response, and helpers that answer. Nothing more -- and the "nothing more" is the point. There is no database handle here, no repository, no Grant. A controller that could reach the data layer would be a controller that skipped the service, and therefore the policy, and `aru doctor` exists to catch exactly that.

It is a struct rather than an interface because it has no second implementation and never will. One way to do one thing.

func (*Context) Ctx added in v0.11.0

func (c *Context) Ctx() context.Context

Ctx returns the request context, which carries the Collector, the logger and the request id.

func (*Context) Fragment added in v0.11.0

func (c *Context) Fragment(status int, name string, data any) error

Fragment renders a partial with a status, for HTMX.

The status matters: a form that failed validation answers 422 with the form fragment, and HTMX swaps it in. Answering 200 would make the browser and the logs both believe it worked.

func (*Context) Input added in v0.11.0

func (c *Context) Input(name string) string

Input reads a form field, from the body or the query string.

Named Input rather than Form because Input is the word the vocabulary already uses for it, and the vocabulary is the point (RULE 10).

func (*Context) JSON added in v0.11.0

func (c *Context) JSON(status int, v any) error

JSON answers with JSON. It exists for the endpoints that are genuinely an API; a page answers with View.

func (*Context) Param added in v0.11.0

func (c *Context) Param(name string) string

Param reads a path parameter: /invoices/{id} gives Param("id").

func (*Context) Query added in v0.11.0

func (c *Context) Query(name string) string

Query reads a query string parameter.

func (*Context) Redirect added in v0.11.0

func (c *Context) Redirect(to string) error

Redirect answers a redirect, and does the right thing under HTMX.

An HTMX request that gets a 302 follows it inside the fragment, so the whole page ends up nested in a div. HX-Redirect is the header that makes the browser navigate instead. Handling it here means no application has to remember.

func (*Context) Status added in v0.11.0

func (c *Context) Status(code int) error

Status answers with a status and no body.

func (*Context) URL added in v0.15.0

func (c *Context) URL(name string, params ...string) string

URL is the path of a named route, with its parameters filled in order.

ctx.URL("posts.show", post.ID)   -> "/posts/01J.../"

It is what a controller hands a view instead of building a path by hand. "/posts/"+id compiles and keeps compiling after the route moves; this stops working the moment the name is wrong, and says so.

An unknown name or a wrong number of parameters returns empty and is logged at ERROR with the name. Empty is what the views already treat as "there is no link here" -- a page with a missing button is recoverable, and a template renderer that panics takes the whole page down to report something a missing link would have said better.

func (*Context) View added in v0.11.0

func (c *Context) View(name string, data any) error

View renders a page. The data is a typed struct, never a map.

return ctx.View("invoices/index", IndexData{Invoices: list})

A map would compile and render blank on a typo, which is the failure this framework exists to make impossible. `aru doctor` refuses a map here.

type Creator added in v0.11.0

type Creator interface {
	Create(*Context) error
}

Creator answers GET /thing/create -- the empty form.

type Destroyer added in v0.11.0

type Destroyer interface {
	Destroy(*Context) error
}

Destroyer answers DELETE /thing/{id}.

type Editor added in v0.11.0

type Editor interface {
	Edit(*Context) error
}

Editor answers GET /thing/{id}/edit -- the filled form.

type Indexer added in v0.11.0

type Indexer interface {
	Index(*Context) error
}

Indexer answers GET /thing -- the list.

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware is the standard net/http signature. We do not invent our own type: that is what keeps the whole Go ecosystem compatible with the framework.

type Renderer added in v0.11.0

type Renderer interface {
	Render(ctx context.Context, w http.ResponseWriter, status int, name string, data any) error
}

Renderer draws a named view with typed data.

It is an interface here, and implemented in the view package, for one reason: the view package imports httpx to register its route, so httpx importing the view package back would be a cycle. The kernel wires the concrete one at boot.

type Route

type Route struct {
	Method  string
	Pattern string
	Module  string
	// contains filtered or unexported fields
}

Route is metadata, used by `aru routes` and by the error page.

func (*Route) Name

func (r *Route) Name(name string) *Route

Name gives the route a name, so a URL can be generated from it instead of written by hand.

Route.Get("/", home).Name("home")
Route.Resource("invoices", InvoiceController{})   // names them all

It returns the route so the call chains, and the declaration reads as one line: Route.Get("/", home).Name("home").

The name was a field on Route from the first version and was never filled in. A field that nothing writes is a promise the code does not keep -- `aru routes` printed an empty column, and there was no way to generate a URL.

func (*Route) RouteName added in v0.11.0

func (r *Route) RouteName() string

RouteName returns the name given with .Name, or empty.

The exported field used to be called Name and nothing ever wrote to it -- a promise the code did not keep. Now .Name(...) writes it and this reads it.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router is a thin shell over http.ServeMux, which since Go 1.22 already handles methods and path parameters. It exists for groups, per-group middleware and route metadata -- the metadata is what lets the CLI generate typed URL helpers and what the error page uses to show the matched route.

func NewRouter

func NewRouter() *Router

NewRouter returns an empty router.

func (*Router) Action added in v0.11.0

func (r *Router) Action(method, pattern string, h func(*Context) error, mws ...Middleware) *Route

Action registers one controller action, for a route outside a resource.

Route.Action("GET", "/dashboard", dashboard.Index).Name("dashboard")

func (*Router) Delete

func (r *Router) Delete(pattern string, h http.HandlerFunc, mws ...Middleware) *Route

Delete registers a DELETE route.

func (*Router) ForModule

func (r *Router) ForModule(name string) *Router

ForModule returns a sub-router that tags its routes with the module name, so `aru routes` can group them. The Kernel calls it for each module.

func (*Router) Get

func (r *Router) Get(pattern string, h http.HandlerFunc, mws ...Middleware) *Route

Get registers a GET route.

func (*Router) Group

func (r *Router) Group(prefix string, mws ...Middleware) *Router

Group returns a sub-router with the prefix appended and the middleware inherited. The route table is shared with the parent.

func (*Router) Patch

func (r *Router) Patch(pattern string, h http.HandlerFunc, mws ...Middleware) *Route

Patch registers a PATCH route.

func (*Router) Post

func (r *Router) Post(pattern string, h http.HandlerFunc, mws ...Middleware) *Route

Post registers a POST route.

func (*Router) Put

func (r *Router) Put(pattern string, h http.HandlerFunc, mws ...Middleware) *Route

Put registers a PUT route.

func (*Router) Resource added in v0.11.0

func (r *Router) Resource(name string, controller any) []*Route

Resource registers the REST routes a controller implements.

Route.Resource("invoices", InvoiceController{})

The seven, in the conventional order and with the conventional names:

GET    /invoices             index    invoices.index
GET    /invoices/create      create   invoices.create
POST   /invoices             store    invoices.store
GET    /invoices/{id}        show     invoices.show
GET    /invoices/{id}/edit   edit     invoices.edit
PUT    /invoices/{id}        update   invoices.update
PATCH  /invoices/{id}        update   invoices.update
DELETE /invoices/{id}        destroy  invoices.destroy

The order matters: /invoices/create is registered before /invoices/{id} so a GET of "create" reaches the form rather than being read as an id. Go's ServeMux prefers the more specific pattern, and registering in this order keeps the intent readable even where the mux would sort it out anyway.

A controller implementing none of the seven registers nothing and returns zero routes, which is a wiring mistake worth seeing in `aru routes`.

func (*Router) Routes

func (r *Router) Routes() []*Route

Routes returns the registered routes, in registration order.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP dispatches to the underlying mux.

func (*Router) Table added in v0.11.0

func (r *Router) Table() *Routes

Table returns the route table, for URL generation and for `aru routes`.

func (*Router) WithRenderer added in v0.11.0

func (r *Router) WithRenderer(rd Renderer) *Router

WithRenderer returns a router whose handlers can render views.

The kernel calls it at boot with the view module. Without it, Context.View returns an error naming the missing line in bootstrap/app.go rather than panicking.

type Routes added in v0.11.0

type Routes struct {
	// contains filtered or unexported fields
}

Routes is the table of registered routes, and the index by name.

func (*Routes) All added in v0.11.0

func (t *Routes) All() []*Route

All returns the routes in registration order, for `aru routes`.

func (*Routes) Must added in v0.11.0

func (t *Routes) Must(name string, params ...string) string

Must is URL for the places that cannot handle an error -- a template helper, mostly. It returns the message as the href, so a broken link says what is wrong instead of pointing at "/".

func (*Routes) URL added in v0.11.0

func (t *Routes) URL(name string, params ...string) (string, error)

URL builds the path of a named route, filling the parameters in order.

URL("home")                  -> "/"
URL("invoices.show", "42")   -> "/invoices/42"

A hardcoded "/invoices/"+id compiles and keeps compiling after the route moves. This does not: an unknown name or a wrong number of parameters is an error the caller sees, not a 404 the user sees.

It returns an error rather than panicking, because a URL is often built from data -- and a panic in a template renderer takes the whole page down to report something a broken link would have said better.

type Shower added in v0.11.0

type Shower interface {
	Show(*Context) error
}

Shower answers GET /thing/{id} -- one record.

type Storer added in v0.11.0

type Storer interface {
	Store(*Context) error
}

Storer answers POST /thing -- the form submission.

type Updater added in v0.11.0

type Updater interface {
	Update(*Context) error
}

Updater answers PUT and PATCH /thing/{id}.

Directories

Path Synopsis
Package middleware holds the mandatory request pipeline.
Package middleware holds the mandatory request pipeline.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL