Context values

May 22, 2025

I’ve already talked a bit about context values, and when not to use them. For a recap, take a look at these previous posts, so I don’t have to rehash those points today:

So from here on out, we’re assuming that you have a legitimate reason to store values in a context. How can/should you go about it? Read on!

type Context

type Context interface {
…
	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key. Successive calls to Value with
	// the same key returns the same result.
	//
	// Use context values only for request-scoped data that transits
	// processes and API boundaries, not for passing optional parameters to
	// functions.

We’ll stop here, to introduce a simple example. Let’s say you want to store the id of an authenticated user in the context, for use in various HTTP handlers—a very reasonable thing to do.

// Add the user ID to the context...
ctx := context.WithValue(ctx, "user_id", userID)

Then later, to retrieve that value, in an HTTP handler, for instance:

userID, ok := ctx.Value("user_id").(string)
if !ok {
	return errors.New("no user ID found")
}

A couple things to note:

  • The return type of ctx.Value(...) is the empty interface (any), thus necessitating a type assertion (unless the value you’ve retrieved is usable as an any, of course).
  • Both the getter (ctx.Value(...) and setter (context.WithValue(...)) depend on the key ("user_id" in this case). It’s best to define that in a single place. A const would do in this case, but there are better alternatives we’ll get to soon.

Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Context key/value utilities

So we’ve finally settled on a suitable type for our context key types: string, or maybe a non-empty struct. We also understand that to guard against key collisions, we need our custom type to be un-exported. type contextKey string const KeyUserID = contextKey("user_id") Now code anywhere in your project can use the context key without fear of collision. Done! Right? I’m sure you know the fact that I’m asking means there’s more we can do.


Context key type: Final recommendation

First, a correction! An astute reader pointed out that I made a small mistake in my post on June, 10, with regard to string context keys. My code example showed: type contextKey string const ( KeyUserID = "user_id" KeyTransactionID = "transaction_id" KeyMessageID = "message_id" ) But it should have been: type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) This is a nasty kind of bug, because the code will continue to work as expected—just without any protection from key collisions!


Struct context keys

First off, an apology for being rather unreliable with my “daily” emails. I’m in the middle of some traveling (was in the UK and Netherlands last week, and will be on a US road trip starting tomorrow), so I’m writing when I have a spare moment, which isn’t that often. I’ve been talking about finding the ideal type for a context key. So far I’ve looked at empty structs (e.g. struct{}), integers, and strings.

Get daily content like this in your inbox!

Subscribe