Getting started
injecute is a dependency injection container for TypeScript without decorators. You register each service with a key, a factory (a function or a class) and the keys it depends on. The container resolves everything in the right order, and its type grows with every registration.
Install
sh
npm install injecutesh
pnpm add injecutesh
bun add injecutesh
deno add npm:injecuteinjecute is an ES module for Node.js ≥ 22.12, Deno, Bun and browsers, with no runtime dependencies. It also works without a build step.
Your first container
ts
import { DIContainer } from 'injecute';
class Database {
constructor(readonly url: string) {}
query(sql: string): string[] {
return [`${this.url}: ${sql}`];
}
}
class UserRepository {
constructor(private readonly db: Database) {}
findAll(): string[] {
return this.db.query('select * from users');
}
}
const app = new DIContainer()
.addInstance('dbUrl', 'postgres://localhost/app')
.addSingleton('db', Database, ['dbUrl'])
.addSingleton('users', UserRepository, ['db']);
const users = app.get('users'); // typed as UserRepository
users.findAll();What happened:
addInstance('dbUrl', …)registers an existing value.addSingleton('db', Database, ['dbUrl'])registers a class. The container callsnew Database(dbUrl)the first timedbis needed, then reuses that instance.app.get('users')resolvesusersand everything it depends on. Its type isUserRepository, inferred from the registration.
Factory parameters and results are inferred from the registrations:
ts
import { DIContainer } from 'injecute';
const app = new DIContainer()
.addInstance('port', 8080)
// port: number
.addSingleton('address', (port) => `http://localhost:${port}`, ['port']);
const address = app.get('address'); // string
Mistakes are compile errors. A typo in a dependency key is reported on the key:
ts
app.addSingleton('server', createServer, ['adress']);
// Type '"adress"' is not assignable to type 'Dependency<…>'. Did you mean '"address"'?Next steps
- Registering services: lifetimes, optional dependencies, aliases, decoration.
- Containers, forks and modules: forks, isolated forks for tests, modules.
- Request context: the trace id, tenant and user of the current request in every service.
- Scoped state: scopes for a session, a request or a transaction, and the patterns that keep every service in the root.
- Lifecycle and dispose: releasing connections with
dispose()andawait using. - Frameworks: Express, Fastify, Hono, Next.js and GraphQL; React, Vue and Solid; coming from NestJS.
- Roles: which part of your code gets which view of the container.