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
- Always pass context as the first parameter
- Never store context in a struct
- Always call
cancel()when usingWithCancelorWithTimeout - 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.