docs

Request Scope Store

The Request Scope Store is a utility that helps manage and propagate context-specific data (such as user information, request IDs, or feature flags) across asynchronous operations without manual parameter passing.

In complex systems, this utility ensures that each request or logical operation maintains its own isolated state, preventing data leaks and simplifying access to context throughout the application lifecycle.

Why Use Request Scope Store?

Use Cases

The Request Scope Store is particularly useful for:

Creating a Store

You can create a request scope store by using the createStore function and defining the shape of your store using TypeScript generics.

import { createStore } from '@koala-ts/framework';

type RequestScope = {
  userId: string;
  requestId: string;
};

const requestScopeStore = createStore<RequestScope>();

API Reference

run(initialState, callback)

Initializes a new scope with the given state and runs the callback within that context.

requestScopeStore.run({ userId: 'anonymous', requestId: 'req-123' }, () => {
  // Your code runs within this scope
  console.log(requestScopeStore.get('userId')); // 'anonymous'
});

set(key, value)

Updates a value in the current scope.

requestScopeStore.set('userId', 'user-42');

get(key)

Retrieves a value from the current scope.

const userId = requestScopeStore.get('userId');
const requestId = requestScopeStore.get('requestId');

all()

Returns the entire current scope state.

const scope = requestScopeStore.all();
console.log(scope); // { userId: 'user-42', requestId: 'req-123' }

has(key)

Checks if a specific key exists in the current scope.

if (requestScopeStore.has('userId')) {
  const userId = requestScopeStore.get('userId');
}

Usage Example

Here’s a complete example of how to use the Request Scope Store in your application:

import { createStore, type HttpScope, Route } from '@koala-ts/framework';

type RequestScope = {
  userId: string;
  requestId: string;
};

const requestScopeStore = createStore<RequestScope>();

export class UserController {
  @Route({method: 'GET', path: '/users/:id'})
  show({request}: HttpScope) {
    // Initialize the scope with default values
    requestScopeStore.run({ userId: 'anonymous', requestId: 'req-123' }, () => {
      // Set userId after authentication
      requestScopeStore.set('userId', request.params.id);

      // Access anywhere in the same async context
      const userId = requestScopeStore.get('userId');
      const requestId = requestScopeStore.get('requestId');

      // Get the whole state
      const scope = requestScopeStore.all();

      // Use the data in your business logic
      console.log(`Processing request ${requestId} for user ${userId}`);
    });
  }
}

With Middleware

You can also use the Request Scope Store in middleware to set up context that will be available to all subsequent handlers:

import { createStore, type HttpScope, type NextMiddleware } from '@koala-ts/framework';

type RequestScope = {
  userId: string;
  requestId: string;
  timestamp: number;
};

const requestScopeStore = createStore<RequestScope>();

export async function requestContextMiddleware(scope: HttpScope, next: NextMiddleware): Promise<void> {
  const requestId = scope.request.headers['x-request-id'] || crypto.randomUUID();
  
  await requestScopeStore.run(
    { 
      userId: 'anonymous', 
      requestId, 
      timestamp: Date.now() 
    }, 
    next
  );
}
    { 
      userId: 'anonymous', 
      requestId, 
      timestamp: Date.now() 
    }, 
    async () => {
      await next();
    }
  );
}

Then in your controller, you can access the data set by the middleware:

export class UserController {
  @Route({method: 'GET', path: '/profile', middleware: [requestContextMiddleware]})
  profile({request}: HttpScope) {
    // Access the request context set by the middleware
    const requestId = requestScopeStore.get('requestId');
    const timestamp = requestScopeStore.get('timestamp');
    
    console.log(`Request ${requestId} started at ${timestamp}`);
  }
}