Go

context.WithValue — Request-Scoped Values

admin by @admin ADMIN
18h ago
May 31, 2026
Public
0 0 up · 0 down Sign in to vote
Attach data to a context that flows down through your call chain — request IDs, authenticated user, trace spans. Use a private key type to avoid collisions across packages.
Go
Raw
package main

import (
    "context"
    "fmt"
    "log/slog"
    "net/http"
)

// Private key type — guaranteed unique across packages.
type ctxKey struct{ name string }

var (
    userIDKey    = &ctxKey{name: "user_id"}
    requestIDKey = &ctxKey{name: "request_id"}
)

func WithUserID(ctx context.Context, id int) context.Context {
    return context.WithValue(ctx, userIDKey, id)
}

func UserIDFrom(ctx context.Context) (int, bool) {
    id, ok := ctx.Value(userIDKey).(int)
    return id, ok
}

// Middleware that attaches an auto-generated request ID
func RequestIDMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        rid := "req-" + r.Header.Get("X-Request-ID")     // or generate
        ctx := context.WithValue(r.Context(), requestIDKey, rid)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// Anywhere downstream
func handleRequest(w http.ResponseWriter, r *http.Request) {
    rid, _ := r.Context().Value(requestIDKey).(string)
    if uid, ok := UserIDFrom(r.Context()); ok {
        slog.Info("handling", "request_id", rid, "user_id", uid)
    }
    fmt.Fprintln(w, "ok")
}
Tags

Save your own code snippets

Create a free account and build your private vault. Share publicly whenever you want.