Golang Context With Value Best Practices

Published on April 4, 2023

Explain with examples how to use advantages of context values in modern applications, mitigate and control downsides.

Sample package using context values

Context package allows creates new context with associated key - value pair. Consider following example and dive into details

package trace

import "context"

// unexported type for context key
type traceIDContextKey struct{}

// WithTrace - accepts string typed value and returns new context
func WithTrace(ctx context.Context, traceID string) context.Context {
	return context.WithValue(ctx, traceIDContextKey{}, traceID)
}

// FromContext - value extractor method, extracts value and assert type
func FromContext(ctx context.Context) (string, bool) {
	value, ok := ctx.Value(traceIDContextKey{}).(string)

	return value, ok
}

This code snipped declares trace package, serving for storing trace identifier in a context. Usage example:

// Create context with value
ctx := trace.WithTrace(context.Background(), "my_trace_id")

// Extract value from context
traceID, valid := trace.FromContext(ctx)

Use unexported (private) keys

Using unexported type type traceIDContextKey struct{} for keys will prevent direct access to context value. This approach protect from overriding values associated with this key.

Allow compile-time check for setting value

Special constructor method accepts strongly typed value. This approach enable compile-time checks for context value. func WithTrace(ctx context.Context, traceID string) context.Context - accepts only string value.

Assert type on getting value

Hidden key prevents direct access to context value. There is func FromContext(ctx context.Context) (string, bool) - that safely extract and returns trace ID value from context. Default values also could be implemented here.

Don’t use context for business data

Use context only for framework data. It is a bad idea to store business data in a context.

Keep in mind about thread safety

Contexts are trade safe. Your values also may be used in several goroutines simultaneously. Think twice if you are going to pass pointers in context values.