Middleware
Middleware is the mechanism Plumego uses to handle cross-cutting concerns.
Examples include:
- Logging
- Authentication
- Authorization
- Tracing
- Panic recovery
- Rate limiting
Middleware sits between routing and handler execution, and operates on the same request-scoped Context.
Understanding middleware is essential for building correct and maintainable Plumego applications.
Where Middleware Fits in the Lifecycle
Middleware executes after routing and before the handler.
The simplified lifecycle is:
Request
→ Router
→ Middleware Chain
→ Handler
← Middleware Chain
← Response
Middleware wraps the handler and can execute logic:
- Before the handler
- After the handler
- Instead of the handler (by short-circuiting)
Middleware Execution Order
Middleware execution order is explicit and deterministic.
Given two middleware functions, A and B, registered in this order:
app.Use(A)
app.Use(B)
The execution order is:
Request →
A →
B →
Handler →
B →
A →
Response
Key properties:
- Order is defined by registration order
- There is no reordering or prioritization
- Understanding order requires reading the registration code — nothing else
This predictability is intentional.
The Middleware Contract
Conceptually, a Plumego middleware:
- Receives a
*plumego.Context - Decides whether to continue the chain
- Optionally performs work before and after the next step
Although the exact function signature is defined elsewhere, the conceptual contract is:
func(ctx *Context, next NextFunc)
The middleware must explicitly call next() to continue request processing.
If next() is not called, the request stops there.
Short-Circuiting Requests
Middleware may decide to stop request processing early.
Common examples:
- Authentication failure
- Authorization denial
- Rate limit exceeded
In these cases, middleware typically:
- Writes a response
- Returns without calling
next()
Once the response is written, the handler will not run.
This behavior is explicit and visible in the middleware code.
Middleware and Context Mutation
Middleware commonly enriches the context with request-scoped data:
- User identity
- Permissions
- Trace identifiers
- Request metadata
Guidelines:
- Set values early in the chain
- Use immutable or simple data
- Avoid overwriting existing values unless intentional
All middleware and the handler share the same Context instance.
Middleware Responsibilities
Middleware is appropriate for concerns that are:
- Orthogonal to business logic
- Shared across multiple routes
- Independent of specific domain rules
Good middleware responsibilities include:
- Logging request/response metadata
- Injecting authentication information
- Enforcing access control
- Handling panics
- Measuring latency
What Middleware Should Not Do
Plumego intentionally discourages using middleware for:
- Core business logic
- Complex decision-making tied to domain rules
- Data persistence
- Orchestrating workflows
If middleware becomes deeply aware of business rules, boundaries are being violated.
Middleware vs Handler Logic
A useful rule of thumb:
- Middleware answers “can this request proceed?”
- Handlers answer “what does this request do?”
If logic answers the second question, it likely belongs in the handler or domain layer.
Error Handling in Middleware
Middleware may encounter errors.
Common strategies include:
- Writing an error response and stopping the chain
- Attaching error information to the context
- Deferring error handling to later middleware
Plumego does not enforce a single error-handling strategy.
However:
- Error handling must be explicit
- Hidden or implicit recovery is discouraged
Panic Recovery as Middleware
Panic recovery is a classic middleware use case.
A recovery middleware typically:
- Uses
defer - Recovers from panics
- Logs the error
- Writes a safe error response
This keeps panic handling:
- Centralized
- Predictable
- Separated from business logic
Middleware and Performance
Middleware adds overhead.
Plumego’s design minimizes this by:
- Avoiding reflection
- Avoiding dynamic dispatch
- Using simple function calls
However, excessive middleware layers still increase latency.
Guidelines:
- Keep middleware lightweight
- Avoid heavy computation
- Push expensive work deeper only when necessary
Middleware Composition Over Inheritance
Plumego favors composition, not inheritance or global hooks.
Instead of:
- One large middleware doing many things
Prefer:
- Small, focused middleware functions
- Clear composition order
- Explicit intent
This improves readability and testability.
Common Mistakes
Forgetting to Call next()
If next() is not called, the handler will never run.
This is a common source of confusion.
Always verify control flow explicitly.
Writing Responses After next()
If middleware writes a response after calling next(),
ensure that the handler has not already written one.
Double writes lead to undefined behavior.
Using Middleware as a Dependency Injector
Context is not a service container.
Middleware should not inject large dependency graphs into context.
Summary
In Plumego, middleware is:
- Explicit
- Ordered
- Predictable
- Powerful, but intentionally constrained
Middleware handles cross-cutting concerns and nothing else.
Used correctly, it keeps handlers clean and systems understandable.
Used incorrectly, it becomes a source of hidden complexity.
Next
With middleware understood, the next concept is:
→ Handler
This explains the role of handlers as the boundary between HTTP and application logic.