How to use the RequestContext in Base

The per-request object that carries what a handler needs, reached the framework way through injection.

Building APIs
AhraJuly 22, 2026

How to use the RequestContext in Base

Every request carries information your code needs: who is calling, what they asked for, the headers they sent, and a place to stash values that later parts of the request should be able to read. In Base, that information lives in the RequestContext, and you reach it the same way you reach everything else, by asking for it.

What it is

The RequestContext is a safe, structured view of the current request. It gives your application the metadata and capabilities it usually needs (the request's URL and method, caller information, access to dependency injection, and control over the response) without handing you the raw request object. That boundary is deliberate. The raw request exposes the body and stream methods in ways that make accidental data leaks easy. The RequestContext exposes the projection your code should use instead.

The framework owns most of what is on it. The fields it populates, like the request id and the URL, are read-only from your perspective. You read them; you do not set them. What you can write to is your own typed extension bag, which is the part that makes the context genuinely useful across a request.

How you reach it

You get the RequestContext by injecting it into a method with the @InjectRequestContext() parameter decorator. That works in a route handler and in a GraphQL resolver alike, which is the point: the same request context is reachable from both, so cross-cutting request information is not trapped in one dispatcher. You ask for it where you need it, and Base hands you the context for the request in flight.

The typed extension bag

The most powerful part of the RequestContext is the part you write. Module-specific concerns (a device id, a session, the authenticated account) do not belong as hard-coded fields on the context, because the framework cannot know about every application's concepts. Instead, the context carries a typed extension bag, and you read and write it through keys.

You define a key for the value you want to carry, and then you can set it, get it, and require it. A middleware early in the request can resolve the authenticated account and set it on the context under a key. A handler later in the request can require that key and receive the account, typed, with the guarantee that it was set. The key carries the type, so the value you get out is the value you put in, checked by the compiler.

This is how a request accumulates meaning as it flows. The raw request arrives with headers; your middleware turns those into your application's concepts and stores them on the context; your handlers read those concepts back without re-parsing the request or threading values through every function call.

The mental model to keep

The RequestContext is the request, projected into what your application safely needs: read-only framework fields for the request's own metadata, dependency-injection and response control, and a typed extension bag you write through keys. Inject it with @InjectRequestContext() wherever you need it, set your application's concepts onto it once, and read them back anywhere downstream. It is the request's shared, typed scratch space, and it is the same ask-for-what-you-need model as the rest of Base.