← Home
gobackend

Understanding the Context Package in Go


What is Context?

The context package provides a way to carry deadlines, cancellation signals, and request-scoped values across API boundaries and goroutines.

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    result, err := fetchData(ctx)
    // ...
}

Cancellation

Use context.WithCancel to create a context that can be manually cancelled:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
    select {
    case <-ctx.Done():
        fmt.Println("cancelled:", ctx.Err())
        return
    case result := <-doWork():
        fmt.Println("result:", result)
    }
}()

Timeouts

context.WithTimeout sets an automatic deadline:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

resp, err := http.NewRequestWithContext(ctx, "GET", url, nil)

If the request takes more than 5 seconds, it’s automatically cancelled.

Passing Values

You can attach key-value pairs to a context, commonly used for request IDs and authentication:

type ctxKey string

const requestIDKey ctxKey = "requestID"

func WithRequestID(ctx context.Context, id string) context.Context {
    return context.WithValue(ctx, requestIDKey, id)
}

func GetRequestID(ctx context.Context) string {
    id, _ := ctx.Value(requestIDKey).(string)
    return id
}

Best Practices

  1. Always pass context as the first parameter
  2. Never store context in a struct
  3. Always call cancel() when using WithCancel or WithTimeout
  4. Don’t pass nil context — use context.TODO() if unsure

Summary

The context package is fundamental to writing production Go services. It enables graceful cancellation, prevents goroutine leaks, and carries metadata through your call chain.