Overview
The Dedalus Go SDK supports custom middleware to intercept, modify, and observe HTTP requests and responses. Middleware is useful for logging, authentication, error handling, and request/response transformation.
What is Middleware?
Middleware is a function that:
Receives an HTTP request
Optionally modifies the request
Passes the request to the next middleware or handler
Receives the HTTP response
Optionally modifies the response
Returns the response
Middleware executes in the order it’s provided, forming a chain where each middleware can pass the request to the next.
Middleware Function Signature
import (
" net/http "
" github.com/dedalus-labs/dedalus-sdk-go/option "
)
// Middleware function signature
type Middleware func (
req * http . Request ,
next option . MiddlewareNext ,
) ( * http . Response , error )
// MiddlewareNext passes the request to the next middleware
type MiddlewareNext func ( * http . Request ) ( * http . Response , error )
Basic Example: Logging Middleware
import (
" log "
" net/http "
" time "
" github.com/dedalus-labs/dedalus-sdk-go "
" github.com/dedalus-labs/dedalus-sdk-go/option "
)
func Logger ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
// Before the request
start := time . Now ()
log . Printf ( "[REQUEST] %s %s " , req . Method , req . URL . Path )
// Forward the request to the next handler
res , err := next ( req )
// After the request
duration := time . Since ( start )
if err != nil {
log . Printf ( "[ERROR] %s %s - %v (took %v )" , req . Method , req . URL . Path , err , duration )
} else {
log . Printf ( "[RESPONSE] %s %s - %d (took %v )" , req . Method , req . URL . Path , res . StatusCode , duration )
}
return res , err
}
func main () {
client := githubcomdedaluslabsdedalussdkgo . NewClient (
option . WithAPIKey ( "your-api-key" ),
option . WithMiddleware ( Logger ),
)
// All requests will be logged
models , _ := client . Models . List ( context . TODO ())
}
The Logger middleware logs both successful and failed requests with timing information.
Client-Level Middleware
Apply middleware to all requests made by the client:
client := githubcomdedaluslabsdedalussdkgo . NewClient (
option . WithAPIKey ( "your-api-key" ),
option . WithMiddleware ( Logger , RateLimiter , ErrorHandler ),
)
Middleware executes in the order provided: Logger → RateLimiter → ErrorHandler → HTTP request
Per-Request Middleware
Override or add middleware for specific requests:
// Client has Logger middleware
client := githubcomdedaluslabsdedalussdkgo . NewClient (
option . WithAPIKey ( "your-api-key" ),
option . WithMiddleware ( Logger ),
)
// This request adds CustomAuth middleware
models , err := client . Models . List (
context . TODO (),
option . WithMiddleware ( CustomAuth ),
)
Per-request middleware executes after client-level middleware in the chain.
Common Middleware Patterns
1. Authentication Middleware
func CustomAuth ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
// Add custom authentication header
req . Header . Set ( "X-Custom-Token" , "my-secret-token" )
// Forward request
return next ( req )
}
2. Request ID Middleware
import " github.com/google/uuid "
func RequestID ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
// Generate and attach request ID
requestID := uuid . New (). String ()
req . Header . Set ( "X-Request-ID" , requestID )
log . Printf ( "[ %s ] Starting request: %s %s " , requestID , req . Method , req . URL . Path )
res , err := next ( req )
log . Printf ( "[ %s ] Completed request" , requestID )
return res , err
}
3. Retry Middleware
import (
" errors "
" time "
)
func RetryOn5xx ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
maxRetries := 3
backoff := 1 * time . Second
var res * http . Response
var err error
for i := 0 ; i < maxRetries ; i ++ {
res , err = next ( req )
// Success or non-5xx error
if err == nil && res . StatusCode < 500 {
return res , nil
}
// Retry with backoff
if i < maxRetries - 1 {
time . Sleep ( backoff )
backoff *= 2 // Exponential backoff
}
}
return res , err
}
4. Response Caching Middleware
import (
" bytes "
" io "
" sync "
)
type Cache struct {
mu sync . RWMutex
store map [ string ] * http . Response
}
func ( c * Cache ) Middleware ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
// Only cache GET requests
if req . Method != http . MethodGet {
return next ( req )
}
cacheKey := req . URL . String ()
// Check cache
c . mu . RLock ()
if cached , ok := c . store [ cacheKey ]; ok {
c . mu . RUnlock ()
log . Printf ( "[CACHE HIT] %s " , cacheKey )
return cached , nil
}
c . mu . RUnlock ()
// Not in cache, make request
res , err := next ( req )
if err != nil || res . StatusCode != 200 {
return res , err
}
// Store in cache
c . mu . Lock ()
c . store [ cacheKey ] = res
c . mu . Unlock ()
log . Printf ( "[CACHE MISS] %s " , cacheKey )
return res , nil
}
5. Error Handling Middleware
import " github.com/dedalus-labs/dedalus-sdk-go "
func ErrorHandler ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
res , err := next ( req )
if err != nil {
// Handle specific error types
var apiErr * githubcomdedaluslabsdedalussdkgo . Error
if errors . As ( err , & apiErr ) {
switch apiErr . StatusCode {
case 401 :
log . Println ( "Authentication failed - check API key" )
case 429 :
log . Println ( "Rate limit exceeded - backing off" )
case 500 :
log . Println ( "Server error - retrying may help" )
}
}
}
return res , err
}
Built-in Middleware
The SDK provides a debug logging middleware:
import (
" log "
" github.com/dedalus-labs/dedalus-sdk-go "
" github.com/dedalus-labs/dedalus-sdk-go/option "
)
client := githubcomdedaluslabsdedalussdkgo . NewClient (
option . WithAPIKey ( "your-api-key" ),
option . WithDebugLog ( log . Default ()), // Built-in debug middleware
)
WithDebugLog is for development purposes only and should not be used in production. It logs full request and response bodies, including sensitive data.
Complete Example
package main
import (
" context "
" log "
" net/http "
" time "
" github.com/dedalus-labs/dedalus-sdk-go "
" github.com/dedalus-labs/dedalus-sdk-go/option "
" github.com/google/uuid "
)
// Request ID middleware
func RequestID ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
requestID := uuid . New (). String ()
req . Header . Set ( "X-Request-ID" , requestID )
log . Printf ( "[ %s ] → %s %s " , requestID , req . Method , req . URL . Path )
res , err := next ( req )
if err != nil {
log . Printf ( "[ %s ] ✗ Error: %v " , requestID , err )
} else {
log . Printf ( "[ %s ] ✓ %d " , requestID , res . StatusCode )
}
return res , err
}
// Timing middleware
func Timer ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
start := time . Now ()
res , err := next ( req )
duration := time . Since ( start )
log . Printf ( "⏱️ Request took %v " , duration )
return res , err
}
// Custom header middleware
func CustomHeaders ( req * http . Request , next option . MiddlewareNext ) ( * http . Response , error ) {
req . Header . Set ( "X-App-Version" , "1.0.0" )
req . Header . Set ( "X-Environment" , "production" )
return next ( req )
}
func main () {
client := githubcomdedaluslabsdedalussdkgo . NewClient (
option . WithAPIKey ( "your-api-key" ),
option . WithMiddleware (
RequestID , // First: Add request ID
Timer , // Second: Time the request
CustomHeaders , // Third: Add custom headers
),
)
// Make a request - all middleware will execute
models , err := client . Models . List ( context . TODO ())
if err != nil {
log . Fatalf ( "Failed to list models: %v " , err )
}
log . Printf ( "Found %d models" , len ( models . Data ))
}
Best Practices
Order Matters Middleware executes in the order provided. Place authentication before logging to avoid logging auth tokens.
Always Call Next Always call next(req) to continue the middleware chain, unless you want to short-circuit the request.
Handle Errors Check for errors returned by next() and handle them appropriately.
Keep It Light Middleware executes on every request. Keep middleware logic fast to avoid slowing down all requests.
Troubleshooting
Verify you’re using option.WithMiddleware() correctly
Check that you’re calling next(req) to continue the chain
Ensure middleware is applied to the client or request
Middleware executes in the order provided to WithMiddleware()
Client middleware runs before per-request middleware
Review your middleware chain order
Request Modified Unexpectedly
Check all middleware in the chain for request modifications
Middleware higher in the chain can modify requests for downstream middleware