Golang / GoLang Production Patterns and Web Standards Interview Questions
How does context propagate through an HTTP request lifecycle in Go?
Since Go 1.7, every *http.Request carries a context accessible via r.Context(). This context is cancelled when the client disconnects or the server shuts down. Middleware and handlers should pass this context to all downstream calls β database queries, outbound HTTP calls, gRPC β so they all cancel when the request does.
// The request context is cancelled when the client disconnects func userHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // inherits client-disconnect cancellation // Add a per-request deadline ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() // Pass ctx to ALL downstream blocking calls userID := chi.URLParam(r, "id") user, err := userService.FindByID(ctx, userID) // DB call respects deadline if err != nil { if errors.Is(err, context.DeadlineExceeded) { http.Error(w, "request timed out", http.StatusGatewayTimeout) return } http.Error(w, "internal error", http.StatusInternalServerError) return } json.NewEncoder(w).Encode(user) } // Making an outbound HTTP call with the request context func fetchExternalData(ctx context.Context, url string) ([]byte, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("creating request: %w", err) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("executing request: %w", err) } defer resp.Body.Close() return io.ReadAll(resp.Body) } // Middleware: attach request-scoped values to context func requestIDMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := r.Header.Get("X-Request-ID") if id == "" { id = uuid.New().String() } ctx := context.WithValue(r.Context(), requestIDKey, id) w.Header().Set("X-Request-ID", id) next.ServeHTTP(w, r.WithContext(ctx)) }) }
More Related questions...