
Security News
npm ‘is’ Package Hijacked in Expanding Supply Chain Attack
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
Manage your dependencies with ease and safety. RSDI is a minimal, powerful DI container with full TypeScript support — no decorators or metadata required.
Most DI libraries rely on reflect-metadata and decorators to auto-wire dependencies. But this tightly couples your business logic to a framework — and adds complexity:
@injectable()
class Foo {
constructor(@inject("Database") private database?: Database) {}
}
// Notice how in order to allow the use of the empty constructor new Foo(),
// we need to make the parameters optional, e.g. database?: Database.
Why should your core logic even know it’s injectable?
RSDI avoids this by using explicit factory functions — making your code clean, framework-agnostic, and easier to test.
RSDI avoids this by letting you define dependencies in a simple and clear way — keeping your code clean, decoupled from frameworks, and easy to test.
Use RSDI
when your app grows in complexity:
RSDI
works best when you organize your app as a dependency tree.
A typical backend app might have:
Set up your DI container at the app entry point — from there, all other parts can pull in what they need.
const container = new DIContainer()
.add("a", () => "name1")
.add("bar", () => new Bar())
.add("foo", ({ a, bar}) => new Foo(a, bar));
const { foo } = container; // alternatively container.get("foo");
// sample web application components
export function UserController(
userRegistrator: UserRegistrator,
userRepository: UserRepository,
) {
return {
async create(req: Request, res: Response) {
const user = await userRegistrator.register(req.body);
res.send(user);
},
async list(req: Request) {
const users = await userRepository.findAll(req.body);
res.send(users);
},
};
}
export class UserRegistrator {
public constructor(public readonly userRepository: UserRepository) {}
public async register(userData: SignupData) {
// validate and send sign up email
return this.userRepository.saveNewUser(userData);
}
}
export function MyDbProviderUserRepository(db: DbConnection): UserRepository {
return {
async saveNewUser(userAccountData: SignupData): Promise<void> {
await this.db("insert").insert(userAccountData);
},
};
}
export function buildDbConnection(): DbConnection {
return connectToDb({
/* db credentials */
});
}
Now let’s configure the dependency injection container. Dependencies are only created when they’re actually needed.
Your configureDI
function will declare and connect everything in one place.
import { DIContainer } from "rsdi";
export type AppDIContainer = ReturnType<typeof configureDI>;
export default function configureDI() {
return new DIContainer()
.add("dbConnection", buildDbConnection())
.add("userRepository", ({ dbConnection }) =>
MyDbProviderUserRepository(dbConnection),
)
.add("userRegistrator", ({ userRepository }) => new UserRegistrator(userRepository))
.add("userController", ({ userRepository, userRegistrator}) =>
UserController(userRepository, userRegistrator),
);
}
When a resolver runs for the first time, its result is cached and reused for future calls.
By default, you should always use .add() to register dependencies. If you need to replace an existing one — usually in tests — you can use .update() instead. This avoids accidental overwrites and keeps your setup predictable.
Let's map our web application routes to configured controllers
// configure Express router
export default function configureRouter(
app: core.Express,
diContainer: AppDIContainer,
) {
const { usersController } = diContainer;
app
.route("/users")
.get(usersController.list)
.post(usersController.create);
}
Add configureDI()
in your app’s entry point:
// express.ts
const app = express();
const diContainer = configureDI();
configureRouter(app, diContainer);
app.listen(8000);
🔗 Full example: Express + RSDI
RSDI
uses TypeScript’s type system to validate dependency trees at compile time, not runtime.
This gives you autocomplete and safety without decorators or metadata hacks.
As your application grows, it’s a good idea to split your DI container setup into multiple files. This helps keep your code organized and easier to maintain.
For example, you might have a main diContainer.ts file that sets up the core container, and then separate files like controllers.ts, validators.ts, or dataAccess.ts that each register a group of related dependencies.
This modular approach makes it easier to manage changes, test in isolation, and understand how dependencies are wired across different parts of your app.
// diContainer.ts
export const configureDI = async () => {
return (await buildDatabaseDependencies())
.extend(addDataAccessDependencies)
.extend(addValidators);
}
// addDataAccessDependencies.ts
export type DIWithPool = Awaited<ReturnType<typeof buildDatabaseDependencies>>;
export const addDataAccessDependencies = async () => {
const pool = await createDatabasePool();
const longRunningPool = await createLongRunningDatabasePool();
return new DIContainer()
.add("databasePool", () => pool)
.add("longRunningDatabasePool", () => longRunningPool);
};
// addValidators.ts
export type DIWithValidators = ReturnType<typeof addValidators>;
export const addValidators = (container: DIWithPool) => {
return container
.add('myValidatorA', ({ a, b, c }) => new MyValidatorA(a, b, c))
.add('myValidatorB', ({ a, b, c }) => new MyValidatorB(a, b, c));
};
FAQs
TypeScript dependency injection container. Strong types without decorators.
The npm package rsdi receives a total of 2,191 weekly downloads. As such, rsdi popularity was classified as popular.
We found that rsdi demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
Security News
A critical flaw in the popular npm form-data package could allow HTTP parameter pollution, affecting millions of projects until patched versions are adopted.
Security News
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.