The @revas-hq/kit-context
package provides an immutable, chained context implementation inspired by Go's context.Context
. It allows passing request-scoped values down a call stack or component hierarchy without explicit prop drilling.
This pattern is central to other Revas Kit packages for sharing data like authentication tokens, trace IDs, or request-specific configurations between middleware, endpoints, and transport handlers.
Install the package using npm or yarn:
npm install @revas-hq/kit-context
# or
yarn add @revas-hq/kit-context
- Immutable: Adding values creates a new context instance without modifying the parent (uses structural sharing).
- Chained: Contexts form a chain; value lookups delegate to the parent if a key isn't found locally.
- String Keys: Values are associated with simple string keys.
-
Explicit Value Retrieval: Use
getValue<T>(key)
to retrieve values.
Typically, an empty root context is created at the beginning of a request lifecycle (e.g., within kit-http-react-router).
import { createContext, Context } from "@revas-hq/kit-context";
// Create an empty root context
const rootContext: Context = createContext();
Use the withValue
function to create a new context instance with an added key-value pair. The original context remains unchanged. Values cannot be undefined
.
import { withValue } from "@revas-hq/kit-context";
// Add a userId to the context
const contextWithUser: Context = withValue(rootContext, "userId", "user-123");
// Add request trace ID to the context *derived from contextWithUser*
const contextWithTrace: Context = withValue(contextWithUser, "traceId", "trace-abc");
// rootContext is still empty
// contextWithUser only contains userId
// contextWithTrace contains traceId and delegates to contextWithUser for userId
Use the getValue
method to look up a value by its key. The lookup checks the current context node and then traverses up the parent chain until the key is found or the root is reached.
Important: You must provide the expected type via the generic argument (
<T>
). The context itself cannot guarantee the type associated with a string key at compile time.
// Assuming contextWithTrace from the previous example
// Retrieve traceId (found in the current context node)
const traceId = contextWithTrace.getValue<string>("traceId");
console.log(traceId); // Output: trace-abc
// Retrieve userId (found in the parent context node)
const userId = contextWithTrace.getValue<string>("userId");
console.log(userId); // Output: user-123
// Retrieve a non-existent key
const nonExistent = contextWithTrace.getValue<number>("timeout");
console.log(nonExistent); // Output: undefined
// --- Type Safety Warning ---
// Requesting the wrong type can lead to runtime issues if not handled carefully.
const userIdAsNumber = contextWithTrace.getValue<number>("userId");
// userIdAsNumber is "user-123" at runtime, but typed as number | undefined.
// Operations assuming it's a number will fail!
// console.log(userIdAsNumber * 2); // Runtime Error!
Best Practice: Use constants or Symbols for context keys to avoid typos and manage keys centrally. Ensure consumers request values with the correct expected type.
While getValue
returning undefined
indicates a key wasn't found, there isn't a dedicated has(key)
method in this specific implementation. You can check the result of getValue
:
if (contextWithTrace.getValue<string>("userId") !== undefined) {
console.log("User ID exists in the context chain.");
}
This context implementation relies on string keys for value retrieval. TypeScript cannot statically verify that the type T
you provide to getValue<T>(key)
matches the type of the value originally stored with that key. It is the developer's responsibility to ensure consistency between setting and getting values and their types. Mismatches can lead to runtime errors. Consider using well-defined constants or symbols for keys to mitigate typos.
This project is licensed under the MIT License. See the LICENSE file for details.