I've been building a dependency injection container for Node/TypeScript called Injectus, and I'm at the point where I'd rather have a handful of people actually try it and poke holes in it than keep polishing alone. It's 0.2.x not "adopt this in prod tomorrow" young, but the core is solid and I want real usage before I push it further.
What it is: zero-dependency, decorator-free IoC container. No reflect-metadata, no emitDecoratorMetadata, no build config wrestling.
import { Injector, inject, InjectionToken } from "injectus";
const DB_URL = new InjectionToken<string>("DB_URL");
class Database {
url = inject(DB_URL);
}
class UserService {
db = inject(Database);
findAll() {
return this.db.url;
}
}
const injector = Injector.create({
providers: [
{ provide: DB_URL, useValue: "postgres://localhost/app" },
Database,
UserService,
],
});
injector.resolve(UserService).findAll();
await injector.dispose();
Why I built it: I kept hitting the same friction with existing containers - decorators requiring specific tsconfig/build setups, string or symbol tokens silently colliding, and singleton/scoped lifetime mismatches only surfacing as bugs in production. Injectus's actual differentiators:
-
Decorator-free -
inject()works in field initializers and factory bodies, no metadata reflection required. -
Identity-based tokens - classes and
InjectionTokeninstances are the key, not their string description. Two tokens named"Logger"can't collide. -
Captive dependency detection - resolving a
Singletonthat depends on aScopedbinding throwsCaptiveDependencyErrorat resolve time, not silently in prod. -
Deterministic disposal - LIFO teardown via
Symbol.asyncDispose, multiple failing disposers collected into oneAggregateError, full dependency path exposed root-to-leaf onCircularDependencyError/CaptiveDependencyError.
It also benchmarks well against the two closest points of comparison - awilix (decorator-free) and tsyringe (decorator-based) - resolving the same dependency-graph shape in each case:
What's honestly not done yet, so you know what you're getting into:
- No module system yet - (flat provider arrays only, for now)
- No multi-injection - registering the same token twice currently shadows (last-write-wins)
- No async init hooks / lifecycle ordering yet
- No framework adapters (Express example exists, others don't)