typectx v0.11: Speaking the Same Language
Version 0.11 brings a major, but purely syntactic, shift to typectx. I've decided to move away from the "supply chain" metaphor and embrace standard Dependency Injection terminology.
When I first created typectx, I used terms like supplier and product because they fit the mental model of a functional, decoupled supply chain. But over time, it became clear that this bespoke vocabulary was adding an unnecessary learning curve for developers coming from classical DI frameworks like NestJS or Spring.
In v0.11, the underlying engine remains exactly the same—it's still a hyper-minimalistic, functionally pure context container—but the API now speaks a language everyone already knows.
Let's dive into the renames.
The Big Rename: Suppliers are now Services
The most visible change is the core factory function. You no longer create a supplier(); you create a service().
Similarly, the different types of nodes in your dependency graph have been renamed to map directly to standard DI concepts:
supplier→service: The fundamental building block.- Request Supplier → Request Service: A service representing an input value from the user's request.
- Product Supplier → App Service: A service whose value is computed by your app from other services.
Here is what the migration looks like in practice:
// Before (v0.10)
import { index, supplier } from "typectx"
const $session = supplier("session").request<{ userId: string }>()
const $todosDb = supplier("todosDb").product({
suppliers: [],
factory: () => new Map<string, string[]>()
})
const $addTodo = supplier("addTodo").product({
suppliers: [$session, $todosDb],
factory:
({ session, todosDb }) =>
(todo: string) => {
// ...
}
})
// After (v0.11)
import { index, service } from "typectx"
const $session = service("session").request<{ userId: string }>()
const $todosDb = service("todosDb").app({
services: [],
factory: () => new Map<string, string[]>()
})
const $addTodo = service("addTodo").app({
services: [$session, $todosDb],
factory:
({ session, todosDb }) =>
(todo: string) => {
// ...
}
})
Notice how suppliers: [...] in the config object is now services: [...], and .product(...) is now .app(...).
Clarifying Inputs and Products
While the structural nodes are now called "services", the terminology for the values they produce has also been clarified:
- The value returned by a Request Service is now formally called an Input (previously just "request data").
- The value returned by an App Service's factory is now called a Product.
The pack() and assemble() methods remain unchanged. You still "pack" your inputs and "assemble" your services. The conceptual flow of building context from the ground up works the exact same way, just with labels that shouldn't require a glossary to understand.
Type Name Updates
If you are explicitly typing your services (which, as mentioned in the v0.10 release, you shouldn't really need to do manually anymore!), the exported types have been updated to match:
ProductSupplier→AppServiceRequestSupplier→RequestServiceMainSupplier→MainService
Why change this now?
typectx aims to make DI "cool again" in a purely functional, TypeScript-first way. It achieves everything that heavy OOP DI containers do—auto-wiring, maximal colocation, context propagation—without decorators or compiler magic.
However, proving that functional DI is better shouldn't require forcing users to learn a completely new domain language. By adopting the standard "service" nomenclature, typectx lowers the barrier to entry while keeping the functional purity that makes it so powerful.
This is a breaking API change, but upgrading is mostly a matter of running a global Find & Replace across your codebase. I expect the API to stabilize from now on, and typectx is getting closer to its v1 release!
npm install typectx@latest @typectx/react@latest