context-inject

A tiny library for dependency injection

Uri Valevski
2 min readJan 22, 2025

You have an object you need to pass into a deep call stack. It can be a singleton or a resource, like a database, a browser for automated scraping, some command-line flag a token for an API, or any other value that you get early and is needed later on.

The common way to handle this is either by passing the value down the call stack — a safe and readable approach but not too ergonomic, as it results in signature duplication (i.e. the same parameters are written again and again along the call stack), or using a global variable — which is unsafe, hard to test (because you can’t mock it for testing) and limits your ability to use the same code in different contexts.

context-inject solves this problem if you’re working in an async context. By leveraging the NodeJS async_hooks API, it lets you wrap any async function with an injector, and everything within the execution of this function will have access to the value using a global getter function. While the getter itself is global, the value it gets varies according to what was injected in this specific call stack. So you get the flexibility of a global variable, with the locality of a value passed down a specific stack.

type MyDatabase = {}; // Some object.

const { inject, access } = context((): MyDatabase => {
throw new Error("not injected!");
});

const g = () => {
// Arbitrarily complex deep nested code...
// Suddenly you need to access the db. Great!
const dbInstance = access();
// Do something with it...
};

const f = () => {
// Arbitrarily complex deep nested code...
return g();
};

// In production

const myDb: MyDatabase = await initDb();
const fWithDbAccess = inject(() => myDb)(f);
await fWithDbAccess();

// In your test

const myDb: MyDatabase = await mockDbForTesting();
const fWithMockDb = inject(() => myDb)(f);
await fWithMockDb();

// If you forget to inject, you will get an explicit exception.
await f(); // throws new Error("not injected!")

It works in nodejs and deno:

npm i async-context
import { context } from "https://deno.land/x/context_inject/src/index.ts";

Source code: https://github.com/uriva/context-inject

--

--

Uri Valevski
Uri Valevski

Written by Uri Valevski

Formerly: Co-founder/CTO of hyro.ai, Googler

No responses yet