Skip to content

ryanflorence/async-provider

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

async-provider

A lightweight, type-safe async context management system for JavaScript applications. Perfect for server-side applications where you need to share request-scoped values across components and functions.

Features

  • πŸ”’ Type-safe: Full TypeScript support with generic type safety
  • 🌟 Simple API: Just three functions - createContext, provide, and pull
  • 🎯 Isolated Values: Values are isolated to specific async contexts - like a request
  • πŸ”— Single provider: No need to nest multiple providers - pass all contexts in a single Map
  • ↩️ Return values: Unlike raw AsyncLocalStorage, easily get return values from your callbacks
  • πŸ’¨ Zero dependencies: Uses Node's built-in AsyncLocalStorage

Installation

npm install @ryanflorence/async-provider

Usage

import { createContext, provide, pull } from "@ryanflorence/async-provider";

// 1. βœ… Create your contexts
let userContext = createContext<User>();
let dbContext = createContext<Database>();

async function myHandleRequest(req: Request) {
  // setup context values
  let user = await myAuthenticateUser(req);
  let db = myCreateDatabaseConnection(user);

  // 2. βœ… Provide contexts
  let html = await provide(
    new Map([
      [userContext, user],
      [dbContext, db],
    ]),
    async () => {
      // run your app code within this context
      let result = await renderApp();
      // Can return values from provider callback
      return result;
    },
  );

  return new Response(html, {
    headers: { "Content-Type": "text/html" },
  });
}

// 3. βœ… Pull context from anywhere
function renderApp() {
  let user = pull(userContext);
  let db = pull(dbContext);

  // Use user and db...
}

Why async-provider?

Particularly useful in server environments (like React Router Loaders, React Server Components, GraphQL servers) where multiple components need access to request-scoped values:

  • User authentication data
  • Database connections
  • Request-specific configuration
  • Feature flags
  • Logging context

Advantages over Alternatives

  • vs. Module imports

    • Module scope can't access request-specific information
    • Values are properly scoped to each request
  • vs. Raw AsyncLocalStorage

    • Simpler API
    • No need to nest multiple providers
    • Type-safe by default
    • Can return values from context scope callback

API

createContext<T>()

Creates a new context object for values of type T.

let userContext = createContext<User>();

provide<T>(contextMap, callback)

Provides values for contexts within the scope of the callback.

await provide(
  new Map([
    [userCtx, currentUser],
    [featureCtx, flags],
  ]),
  async () => {
    // ...
  },
);

// Also accepts arrays of tuples
await provide(
  [
    [userCtx, currentUser],
    [featureCtx, flags],
  ],
  async () => {
    // ...
  },
);

pull<T>(context)

Retrieves the value for a context. Must be called within a provide scope.

let user = pull(userContext);

License

MIT

Contributing

Contributions welcome! Please read our contributing guidelines first.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published