Pipeline Stages
Understand each stage of the Ginject request pipeline — Middleware, Guard, Interceptor, Handler, Exception Filter.
Pipeline Stages
Every request (HTTP or WebSocket) flows through these five stages in order. Each stage has a specific responsibility and can interact with the request at different points in its lifecycle.
Overview
1. Middleware
Purpose
Middleware is for early-stage request processing. Log requests, add data to context, validate basics, short-circuit the pipeline if needed.
Execution Time
- Runs first, before all other stages
- Runs for every request (if not short-circuited)
What It Receives
- Standard library HTTP request/response
next()function to proceed to next stage
What It Can Do
- Log request details (method, path, headers)
- Validate request format
- Add headers to response
- Add data to context (available to downstream stages)
- Call
next()to proceed - Don't call
next()to short-circuit and stop the pipeline
Available For
- HTTP: ✅ Yes
- WebSocket: ✅ Yes (runs during handshake, before connection accepted)
Scope
- Global:
app.BindGlobalMiddlewares(m) - Module:
module.BindMiddleware(m) - Per-handler:
controller.BindMiddleware(m, controller.CREATE)
Example
Use Cases
- Request logging and metrics
- CORS headers
- Helmet (security headers)
- Request ID generation
- Rate limiting setup
- Body parsing configuration
2. Guard
Purpose
Guard is for authorization and access control. Check if the user/connection is allowed to proceed.
Execution Time
- Runs after middleware, before interceptors
- Runs for every request (unless middleware short-circuited)
What It Receives
- Request context with headers, params, body, etc.
What It Can Do
- Check authentication (JWT, session, API key)
- Check authorization (roles, permissions)
- Return
true→ allow, proceed to next stage - Return
false→ deny, return 403 Forbidden (HTTP) or error event (WebSocket)
Available For
- HTTP: ✅ Yes
- WebSocket: ✅ Yes (Ginject inspects signature to route correctly)
Scope
- Global:
app.BindGlobalGuards(g) - Module:
module.BindGuard(g) - Per-handler:
controller.BindGuard(g, controller.CREATE)
Example
Use Cases
- JWT token validation
- Session authentication
- Role-based access control (RBAC)
- API key validation
- Rate limiting (reject if over limit)
- Custom authorization rules
3. Interceptor
Purpose
Interceptor is for pre/post processing and response transformation. Wrap handler execution, modify requests before, transform responses after.
Execution Time
- Pre-processing: Before handler
- Post-processing: After handler (via
Pipe)
What It Receives
- Request context
- Aggregation object (executes handler and post-processes)
What It Can Do
- Pre-process request (validate, enrich, etc.)
- Execute handler via
agg.Pipe() - Post-process response (transform, wrap, filter)
- Short-circuit and return custom response (without calling handler)
Available For
- HTTP: ✅ Yes
- WebSocket: ✅ Yes
Scope
- Global:
app.BindGlobalInterceptors(i) - Module:
module.BindInterceptor(i) - Per-handler:
controller.BindInterceptor(i, controller.READ)
Example
More Complex Example: Caching
Use Cases
- Request/response logging
- Response transformation (wrap in envelope)
- Caching (check before, cache after)
- Request enrichment (add computed data)
- Timeout handling
- Performance metrics
4. Handler
Purpose
Handler is where business logic executes. Fetch data, process, return result.
Execution Time
- Runs after middleware, guard, interceptor (pre)
- Runs before interceptor (post)
What It Receives
What It Can Do
- Access request data (body, params, query, headers, files)
- Call services and providers (dependency injection by type)
- Execute business logic
- Return response data (automatically serialized)
- Panic with exception (caught by exception filter)
Available For
- HTTP: ✅ Yes
- WebSocket: ✅ Yes
Parameters Injected By Type
HTTP handlers:
*ctx.HTTPContext— request contextctx.Body— JSON bodyctx.Param— path parametersctx.Query— query stringctx.Header— HTTP headersctx.Form— form datactx.File— uploaded files- Any
Provider(service, repository) — by struct type
WebSocket handlers:
*ctx.WSContext— connection contextctx.WSPayload— event data- Any
Provider(service, repository) — by struct type
Example
Use Cases
- Fetch data from database
- Create, update, delete records
- Call external APIs
- Compute results
- Validate input
- Throw exceptions on error
5. Exception Filter
Purpose
Exception filter catches panics and converts them to appropriate responses.
Execution Time
- Runs when a panic occurs at any stage
- Catches panics from middleware, guard, interceptor, handler
What It Receives
- Request context
- Exception object with code, message, HTTP status, etc.
What It Can Do
- Log the error
- Convert exception to HTTP response (with status code and JSON)
- Convert exception to WebSocket error event
- Custom error formatting
- Suppress error (don't respond) if needed
Available For
- HTTP: ✅ Yes
- WebSocket: ✅ Yes
Scope
- Global:
app.BindGlobalExceptionFilters(f) - Module:
module.BindExceptionFilter(f) - Per-handler:
controller.BindExceptionFilter(f, controller.CREATE)
Example
Built-in Exceptions
Throw anywhere in the pipeline:
Use Cases
- Log errors for monitoring
- Convert exceptions to consistent response format
- Add error IDs for debugging
- Custom error messages
- Rate limiting responses
Complete Request Flow Example
Here's how all stages work together:
Summary
| Stage | Purpose | When to Use |
|---|---|---|
| Middleware | Early processing, logging | Logging, CORS, request enrichment |
| Guard | Authorization | Auth validation, role checks |
| Interceptor | Pre/post processing | Response wrapping, caching |
| Handler | Business logic | All application logic |
| Exception Filter | Error handling | Error formatting, logging |
Learn these five stages deeply — they apply to every transport Ginject supports.