Skip to main content

ExecutionContext

The eridu-tech/execution-context module provides a type-safe, composable, and environment-agnostic way to store and propagate contextual data (such as request IDs, user info, or tracing metadata) across async boundaries and function calls. IExecutionContext uses symbols internally for reliable context isolation.

Initial configuration​

To begin using the execution-context, you'll need to create and configure an instance:

./samples/execution-context-initial-config.ts
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";

// Create an execution-context instance with an adapter
export const executionContext = new ExecutionContext(
new AlsExecutionContextAdapter(),
);

ExecutionContext basics​

Running code with context​

You can run code within a context boundary, and all context values will be accessible throughout the call chain:

./samples/run.ts
import { executionContext } from "./execution-context-initial-config.js";
import { contextToken } from "eridu-tech/execution-context/contracts";

// Define context tokens with type-safe identifiers
type User = { id: string; name: string };
const userToken = contextToken<User>("user");
const requestIdToken = contextToken<string>("requestId");

function logData(): void {
// Access context values later in the call chain

// { id: "123", name: "Alice" }
const user = executionContext.get(userToken);
// "req-456"
const reqId = executionContext.get(requestIdToken);

console.log("user:", user);
console.log("reqId:", reqId);
}

executionContext.run(() => {
executionContext.put(userToken, { id: "123", name: "Alice" });
executionContext.put(requestIdToken, "req-456");
logData();
});

Binding functions to context​

You can bind a function to the current context, so it always executes with the captured context values:

./samples/bind.ts
import { executionContext } from "./execution-context-initial-config.js";
import { contextToken } from "eridu-tech/execution-context/contracts";

type User = { id: string; name: string };
const userToken = contextToken<User>("user");
const requestIdToken = contextToken<string>("requestId");

executionContext.run(() => {
executionContext.put(userToken, { id: "123", name: "Alice" });
executionContext.put(requestIdToken, "req-456");

const logData = executionContext.bind((msg: string): void => {
// Access context values later in the call chain
const user = executionContext.get(userToken); // { id: "123", name: "Alice" }
const reqId = executionContext.get(requestIdToken); // "req-456"
console.log("message:", msg);
console.log("user:", user);
console.log("reqId:", reqId);
});

logData("hello");
});

Patterns​

Type safety with context tokens​

You can enforce compile-time type safety by defining context tokens with specific types:

./samples/type-safety.ts
import { executionContext } from "./execution-context-initial-config.js";
import { contextToken } from "eridu-tech/execution-context/contracts";

const userToken = contextToken<{ id: string; name: string }>("user");
executionContext.put(userToken, { id: "123", name: "Alice" });
// TypeScript will error if you try to put a value of the wrong type.

Immutable and chainable context operations​

All context mutation methods return the context instance, allowing for method chaining:

./samples/chainable.ts
import { executionContext } from "./execution-context-initial-config.js";
import { contextToken } from "eridu-tech/execution-context/contracts";

type User = { id: string; name: string };
const userToken = contextToken<User>("user");
const requestIdToken = contextToken<string>("requestId");

executionContext
.put(userToken, { id: "123", name: "Alice" })
.put(requestIdToken, "req-456");

Conditional context updates​

You can conditionally update the context:

./samples/conditional.ts
import { executionContext } from "./execution-context-initial-config.js";
import { contextToken } from "eridu-tech/execution-context/contracts";

const userToken = contextToken<{ id: string; name: string }>("user");

executionContext.when(true, (ctx) =>
ctx.put(userToken, { id: "conditional", name: "Bob" }),
);

Adapters​

  • AlsExecutionContextAdapter: Uses Node.js AsyncLocalStorage for async context propagation.
  • NoOpExecutionContextAdapter: No-op adapter for testing or environments without async context support.

Separating reading, updating, and execution concerns​

The library includes several contracts that separate concerns for different use cases:

  • IReadableContext: Read-only access to context values (safe for consumers that should not mutate context).
  • IContext: Adds all mutation methods (put, update, remove, etc.) for full context management.
  • IExecutionContextBase: Adds execution boundary methods (run, bind) for context propagation and isolation.
  • IExecutionContext: Combines all of the above for complete context management and execution.

IExecutionContextBase​

  • run(invocable) — Runs a function within the current execution-context. All context values are accessible during execution.
  • bind(fn) — Returns a new function that, when called, executes the original function within the captured context.

IContext​

  • add(token, value) — Adds a value only if it doesn't already exist. No-op if the key exists.
  • put(token, value) — Sets or overwrites a value for the token.
  • putIncrement(token, settings?) — Initializes (if missing) and increments a numeric value. Optional max cap.
  • putDecrement(token, settings?) — Initializes (if missing) and decrements a numeric value. Optional min floor.
  • putPush(token, ...values) — Initializes (if missing) and pushes values to an array.
  • update(token, value) — Updates a value only if it exists. No-op if missing.
  • updateIncrement(token, settings?) — Increments a numeric value only if it exists. Optional max cap.
  • updateDecrement(token, settings?) — Decrements a numeric value only if it exists. Optional min floor.
  • updatePush(token, ...values) — Pushes values to an array only if it exists. No-op if missing.
  • remove(token) — Removes a value from the context.
  • when(condition, ...invocables) — Conditionally applies operations if the condition is true.

IReadableContext​

  • contains(token, matchValue) — Checks if an array context value contains a specific item or matches a predicate.
  • exists(token) — Checks if a value exists for the token.
  • missing(token) — Checks if a value is missing for the token.
  • get(token) — Retrieves a value or null if not found.
  • getOr(token, defaultValue) — Retrieves a value or returns the provided default if not found.
  • getOrFail(token) — Retrieves a value or throws if not found.

Further information​

For further information refer to eridu-tech/execution-context API docs.