The first fully type-inferred, type-safe and hyper-minimalistic DI solution for Typescript!
No OOP, reflect-metadata, decorators, annotations or compiler magic, just simple functions.
import { createMarket, index } from "commodity"
// Create market and define suppliers
const market = createMarket()
const $$session = market.offer("session").asResource<{ userId: string }>()
const $$api = market.offer("api").asProduct({
suppliers: [$$session],
factory: ($) => new ApiClient($($$session).userId)
})
// Assemble with type safety
const api = $$api
.assemble(index($$session.pack({ userId: "123" })))
.unpack()
// Use it!
const users = await api.getUsers()
Built for modern TypeScript applications that demand performance, safety, and simplicity.
Zero type boilerplate. End-to-end type safety with compile-time dependency validation and no extra type definitions.
Just functions and closures. No OOP, reflect-metadata, decorators, or compiler magic. What you see is what you get.
Smart memoization, lazy loading, and a tiny bundle size (~3KB). Designed for minimal runtime overhead.
Easy mocking and dependency swapping. Swap implementations effortlessly to achieve perfect test isolation.
Promotes SOLID, clean, and code-splittable design patterns that grow with your application.
Works anywhere TypeScript works. Use it in React, Node.js, Deno, or Bunβfrom frontend to backend.
Dependencies are resolved via closures, not global state. This ensures clean, predictable, and easy-to-reason-about behavior in any environment.
A supply chain metaphor (Market, Product, Resource) that makes dependency injection feel natural and easier to understand.
Don't let your past experiences with DI prevent you from trying this solution!
Catch dependency errors before they reach production. Commodity's architecture provides end-to-end type inference, eliminating entire classes of bugs and ensuring your dependency graph is always valid.
const $$config = market.offer("config").asResource<{
api: { baseUrl: string };
}>();
const $$db = market.offer("db").asProduct({
factory: () => new DatabaseClient() // Returns a DatabaseClient instance
});
const $$userService = market.offer("userService").asProduct({
suppliers: [$$config, $$db],
factory: ($) => {
// No explicit types needed! They are all inferred.
const config = $($$config);
// ^? const config: { api: { baseUrl: string } }
// (Inferred from the .asResource<T>() definition)
const db = $($$db);
// ^? const db: DatabaseClient
// (Inferred from the $$db's factory return type)
return {
getUser: (id: string) => db.fetchUser(id, config.api.baseUrl)
};
}
});
Smart memoization: dependencies are created in parallel once per context eagerly, and cached. Or choose lazy loading to defer the creation of expensive services until they are first accessed.
// An expensive service, lazy-loaded for on-demand performance.
const $$reportGenerator = market.offer("reporter").asProduct({
factory: () => {
// This expensive logic runs only ONCE, the first time it's needed.
console.log("π Initializing Report Generator...");
return new ReportGenerator();
},
lazy: true
});
const $$app = market.offer("app").asProduct({
suppliers: [$$reportGenerator],
factory: ($) => (userAction: "view_dashboard" | "generate_report") => {
if (userAction === "generate_report") {
// The generator is created on the first call thanks to lazy loading.
// Subsequent calls within the same context will reuse the
// same, memoized instance without running the factory again.
const reporter = $($$reportGenerator);
reporter.generate();
}
}
});
Isolate components completely. With .prototype(), you can create alternative implementations for testing that remove entire dependency trees, leading to cleaner and more robust tests.
// A product that depends on a real database.
const $$userProfile = market.offer("userProfile").asProduct({
suppliers: [$$db],
factory: ($) => ({
bio: $($$db).fetchBio()
})
});
// For tests, create a prototype with no dependencies.
const mockUserProfile = $$userProfile.prototype({
suppliers: [], // <-- No database needed!
factory: () => ({
bio: "This is a mock bio for testing."
})
});
// The component we want to test.
const $$app = market.offer("app").asProduct({
suppliers: [$$userProfile],
factory: ($) => `<div>${$$(userProfile).bio}</div>`
});
// In the test, just .try() the prototype.
// No need to provide a database connection!
const app = $$app.try(mockUserProfile).assemble().unpack();
From React components to API servers, Commodity adapts to your architecture.
Eliminate prop drilling. Share context across components without global state or complex providers.
Request-scoped context propagation. Clean service layers. Perfect for Express, Fastify, or any framework.
Swap implementations on the fly. Test different strategies. Mock external services with ease.
Join developers who've already made the switch to type-inferred dependency injection!
π Install with npm install commodity