Go SDK · HTTP middleware
The middleware threads a per-request Event through your handler chain, auto-populates Origin from the request and Result from the response status, and lets handlers enrich the event with Action / Target without threading the recorder through each one.
Actor Resolver
The middleware needs an ActorResolver — a function that derives the Actor for a request from its context. Sessions live in cookies, JWTs, headers, OAuth tokens — whatever your auth stack uses. Pair the resolver with a session middleware that plants identity on the request context before the audit middleware runs.
import (
"context"
"net/http"
"github.com/everscribe/sdk-go/pkg/event"
)
type actorIDKey struct{}
// withSession plants the actor's identity on the request context.
// Real apps read a session cookie or JWT here.
func withSession(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-User-ID")
ctx := context.WithValue(r.Context(), actorIDKey{}, id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// actorResolver reads the identity withSession planted and turns it
// into an Actor for the audit middleware.
func actorResolver(users map[string]User) event.ActorResolver {
return func(ctx context.Context) event.Actor {
id, _ := ctx.Value(actorIDKey{}).(string)
if u, ok := users[id]; ok {
return event.Actor{
Type: "user",
ID: u.ID,
DisplayName: u.Name,
Email: u.Email,
}
}
return event.Actor{Type: "anonymous"}
}
}
ActorResolver signature:
type ActorResolver func(ctx context.Context) event.Actor
Wiring it up
Session middleware first, audit middleware second, then your routes. The resolver runs inside the audit middleware and reads what withSession planted:
auditMW := event.NewMiddleware(actorResolver(users))
apiHandler := withSession(auditMW(apiMux))
Ordering matters. If the audit middleware runs first, the resolver sees an empty context and every event records as anonymous.