Buffalo — Interfaces#
Interface catalog#
Context#
- Package:
github.com/gobuffalo/buffalo - File:
context.go:18 - Methods:
// Embeds context.Context (Deadline, Done, Err, Value) Response() http.ResponseWriter Request() *http.Request Session() *Session Cookies() *Cookies Params() ParamValues Param(string) string Set(string, any) LogField(string, any) LogFields(map[string]any) Logger() Logger Bind(any) error Render(int, render.Renderer) error Error(int, error) error Redirect(int, string, ...any) error Data() map[string]any Flash() *Flash File(string) (binding.File, error) - Purpose: Per-request state bag passed to every
Handlerand middleware. Wraps the HTTP writer/request pair, provides session/cookie/flash access, logging, body binding, and response rendering in a single unified surface. - Implementations:
DefaultContext(default_context.go) — verified with compile-time assertionvar _ Context = &DefaultContext{}. - Design quality: Broad (17 methods + inherited context.Context). Deliberately wide by framework design — it is the single thing middleware and handlers receive. This violates strict ISP but is a pragmatic framework choice: handlers rarely use all methods, but having them co-located saves indirection. The stdlib
context.Contextembedding is clean; it makesbuffalo.Contextpassable to any stdlib-aware function expectingcontext.Context.
ParamValues#
- Package:
github.com/gobuffalo/buffalo - File:
context.go:41 - Methods:
Get(string) string - Purpose: Minimal interface for URL/query/form parameter lookup. Satisfied by
url.Valuesfrom stdlib. - Implementations:
url.Values(stdlib) is the primary implementation; the interface allows users to supply custom param sources. - Design quality: Excellent ISP example. One method, one concern. The comment “isn’t it great that you set your own?” signals deliberate extensibility at minimal cost.
render.Renderer#
- Package:
github.com/gobuffalo/buffalo/render - File:
render/renderer.go:7 - Methods:
ContentType() string Render(io.Writer, Data) error - Purpose: Unified contract for all response serialization strategies.
Context.Render(statusCode, Renderer)accepts any value satisfying this interface. - Implementations:
templateRenderer(HTML via plush),downloadRenderer(file download),stringRenderer(plain text),htmlAutoRenderer(content-negotiation auto),funcRenderer(callback-based),sseRenderer(Server-Sent Events). All are unexported structs; theEnginemethods (HTML(),JSON(),String(),Download(),Auto()) serve as factories. - Design quality: Near-perfect example of the Interface Segregation Principle. Two methods, zero dependencies on buffalo internals — a third-party package can implement
Rendererwith zero imports from buffalo. TheDatatype (map[string]any) is the only coupling and it is defined in the same package.
servers.Server#
- Package:
github.com/gobuffalo/buffalo/servers - File:
servers/servers.go:10 - Methods:
Shutdown(context.Context) error Start(context.Context, http.Handler) error SetAddr(string) - Purpose: Abstracts the HTTP serving layer.
App.Serve()callsserver.Start(ctx, app)andserver.Shutdown(ctx)on SIGTERM, without knowing whether the server is plain TCP, TLS, or a pre-created listener. - Implementations:
Simple(wraps*http.Server),TLS(adds cert/key loading),Listener(wraps pre-creatednet.Listener). Factory functionsWrap,WrapTLS,WrapListenercreate them from stdlib types. - Design quality: Well-segregated. Three methods cover the entire lifecycle.
SetAddris slightly awkward (mutating after creation), but needed to inject the address fromOptionsafter the server object is constructed. No compile-time assertion present (unlike Worker and Context).
worker.Worker#
- Package:
github.com/gobuffalo/buffalo/worker - File:
worker/worker.go:14 - Methods:
Start(context.Context) error Stop() error Perform(Job) error PerformAt(Job, time.Time) error PerformIn(Job, time.Duration) error Register(string, Handler) error - Purpose: Background job queue abstraction. Decouples
Appfrom any specific job processing backend. The built-inSimpleimplementation runs jobs in goroutines; the interface allows third-party adapters (e.g., gocraft/work) to be swapped in viaOptions.Worker. - Implementations:
Simple(worker/simple.go) — compile-time assertionvar _ Worker = &Simple{}confirmed. Third-party adapters implement this interface externally. - Design quality: Well-designed, though the project itself acknowledges semantic ambiguity in a TODO comment:
Perform()is named like an executor but acts as an enqueuer. The three-variantPerform/PerformAt/PerformInscheduling API is a reasonable decomposition.Registerdecoupling job names from implementation allows serialization of job types across process restarts.
Resource#
- Package:
github.com/gobuffalo/buffalo - File:
resource.go:28 - Methods:
List(Context) error Show(Context) error Create(Context) error Update(Context) error Destroy(Context) error - Purpose: Convention-based REST resource contract.
App.Resource("/path", r)maps the five CRUD handlers to standard HTTP method+path combinations automatically. - Implementations:
BaseResource(default implementation returning 404 for all methods, used for embedding). User types embedBaseResourceand override specific methods — a template method pattern without using generics or reflection. - Design quality: Fixed five-method surface encodes the “resource = CRUD” convention explicitly. The
Middlercompanion interface (Use() []MiddlewareFunc) allows per-resource middleware declaration without requiringAppto be aware of it. The comment block explaining the middleware-skip interaction with type assertions is telling: the reflection-based middleware identity system requires theResourcevariable to be typed asbuffalo.Resource, not the concrete struct, forSkip()to work — a subtle coupling cost of the reflection approach.
Middler#
- Package:
github.com/gobuffalo/buffalo - File:
resource.go:38 - Methods:
Use() []MiddlewareFunc - Purpose: Optional companion to
Resource. If a resource struct also implementsMiddler,App.Resource()automatically applies the returned middleware to the resource’s route group. - Implementations: User-defined resource structs (opt-in).
- Design quality: Good use of optional interface discovery (
if mm, ok := r.(Middler); ok). KeepsResourcesmall while adding opt-in capability.
binding.Bindable#
- Package:
github.com/gobuffalo/buffalo/binding - File:
binding/bindable.go:8 - Methods:
Bind(*http.Request) error - Purpose: Allows a struct to override the default body-binding logic. When
ctx.Bind(v)is called andvimplementsBindable, the struct’s ownBindmethod is called instead of the framework’s decoder. - Implementations: User-defined model types (opt-in).
- Design quality: Classic “self-binder” pattern. Single method, zero framework coupling (just
*http.Request). Clean escape hatch from the default decoder.
binding.ContenTypeBinder#
- Package:
github.com/gobuffalo/buffalo/binding - File:
binding/types.go:8 - Methods:
BinderFunc() Binder ContentTypes() []string - Purpose: Allows registration of custom body decoders keyed by Content-Type header value. Used internally to register JSON, XML, and form decoders.
- Implementations: Internal binder registrations.
- Design quality: Two-method interface for an extension point that few users will touch. The name
ContenTypeBinderhas a typo (Contenmissing thet) — a minor but persistent API wart.
mail.Sender / mail.BatchSender#
- Package:
github.com/gobuffalo/buffalo/mail - File:
mail/sender.go:4 - Methods (Sender):
Send(Message) error - Methods (BatchSender extends Sender):
Send(Message) error SendBatch(messages ...Message) ([]error, error) - Purpose: Email sending abstraction.
Senderis the minimal contract;BatchSenderextends it for bulk delivery with per-message error reporting. - Implementations: SMTP dialer implementation in
mail/dialer.go. - Design quality: Good embedding pattern —
BatchSenderis a superset ofSender, so anyBatchSendersatisfiesSender. The variadicSendBatchreturning([]error, error)is somewhat unusual but practical for bulk mail scenarios.
Interface patterns#
Size distribution: Lean overall. Most interfaces have 1–3 methods (
ParamValues,Renderer,Bindable,Server,Sender).Workerhas 6 methods.Contextis the outlier at ~17 (including embeddedcontext.Context). Average excludingContext: ~2.5 methods per interface.Embedding:
Contextembedscontext.Context(stdlib) — the single most consequential embedding decision in the codebase. It means buffalo contexts thread naturally through the stdlib ecosystem.BatchSenderembedsSender— clean capability layering.Resourcedoes not embed anything;BaseResourceis a concrete default, not an interface embed.
Implicit satisfaction: All interfaces are satisfied implicitly (no registration, no tagging). Consumers discover capability at runtime via type assertions (
if mm, ok := r.(Middler); ok). Compile-time assertions (var _ Context = &DefaultContext{},var _ Worker = &Simple{}) are used in two key locations to catch regressions during development.Stdlib interfaces used:
context.Context— embedded inbuffalo.Contextio.Writer— parameter inrender.Renderer.Render()http.Handler—Appimplements it (ServeHTTP);servers.Server.Start()accepts ithttp.ResponseWriter— returned byContext.Response()http.Hijacker— implemented byResponsefor WebSocket supportfs.ReadDirFile— implemented byfs.gofor embedded template FS
Key abstractions#
Context— The load-bearing abstraction of the entire framework. Every handler and middleware is written againstContext, not against concrete HTTP types. Broad by design; the cost is that testing handlers requires constructing a fullDefaultContextor a test double for all 17 methods. This is the interface most likely to feel burdensome to mock.render.Renderer— The best-designed interface in the codebase. Two methods, no buffalo dependencies, trivially mockable. The richness of the render ecosystem (7+ implementations) from such a small interface is evidence of how well ISP works here. Third-party renderers are a natural extension point.worker.Worker— The primary DI seam inOptions. Buffalo’s built-in job system is deliberately minimal; the interface exists to make it swappable. The semantic confusion between “enqueue” and “execute” (noted in code TODOs) is the main design debt.servers.Server— Makes TLS, Unix socket, and custom listener configurations first-class viaOptions.Servers []Server. Most users never see this interface, but it is what allows Buffalo to support non-TCP serving without any conditional logic inApp.Resource— The framework’s opinionated REST convention made explicit in Go types. TheBaseResourcedefault implementation pattern (embed and override) trades generics/reflection for simplicity. The interaction with reflection-based middleware identity is the weakest coupling in the design.
Interface-driven extensibility#
Buffalo uses interfaces at three distinct extension layers:
Serving layer (
servers.Server): Users plug in custom HTTP servers by passing[]ServertoServe(). This is how production deployments add TLS termination, custom listeners, or UNIX domain sockets.Rendering layer (
render.Renderer): Any type implementing two methods can be returned fromctx.Render(). Third-party renderers (e.g., a PDF renderer, a MessagePack renderer) require zero changes to the framework.Background jobs (
worker.Worker): TheOptions.Workerfield accepts anyWorkerimplementation. Production deployments typically replace the in-processSimpleworker with a Redis-backed adapter (e.g.,buffalo-gocraft-work), and the framework is entirely unaware of the backend.
The Bindable and ContenTypeBinder interfaces provide escape hatches in the request-decoding pipeline — useful for specialized data types or custom protocols.
What Buffalo does not use interfaces for: routing (gorilla/mux is a concrete dependency), sessions (gorilla/sessions is concrete), or templating (gobuffalo/plush is concrete). These are design choices, not oversights — the framework picks sensible defaults and exposes extension points only where real variability is expected.