context.Context in Go: Timeouts and Cancellation

A
Author
04 Jul, 2026 1 min read 1.28K views

A context.Context carries a cancellation signal and an optional deadline through a chain of function calls. When a user closes the browser tab, every database query and HTTP call made for that request can stop.

Every request already has one

func handleSearch(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()   // cancelled when the client disconnects
    results, err := search(ctx, r.URL.Query().Get("q"))
    if err != nil {
        if errors.Is(err, context.Canceled) {
            return       // client gave up; nothing to send
        }
        http.Error(w, "search failed", http.StatusInternalServerError)
        return
    }
    json.NewEncoder(w).Encode(results)
}

Add a timeout

ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()   // always call cancel to release resources

row := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = $1", id)

Pass it down

func search(ctx context.Context, q string) ([]Result, error) {
    req, _ := http.NewRequestWithContext(ctx, "GET", searchURL(q), nil)
    resp, err := http.DefaultClient.Do(req)   // aborts when ctx is cancelled
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    // ...
}

Respect cancellation in your own loops

for _, item := range items {
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
    }
    process(item)
}

Rules

  • Context is the first parameter, named ctx.
  • Don't store contexts in structs; pass them explicitly.
  • Never pass nil; use context.Background() at the top level or context.TODO() while refactoring.
  • Use context.WithValue only for request-scoped data like trace IDs, not for optional function parameters.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.