Skip to content

serendipious/nodejs-minjex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dependency Injection for Node.js

A simple and intuitive asynchronous Dependency Injection library for Node.js with full TypeScript support and comprehensive test coverage.

Node.js TypeScript Test Coverage

Why Dependency Injection?

Dependency Injection vs. Pub-Sub/Event Listeners

Aspect Dependency Injection Pub-Sub/Event Listeners
Execution Timing Executes when dependencies are available Fires immediately when event is emitted
Dependency Management Creates dependency graph, resolves in correct order No dependency tracking, manual coordination required
Error Handling Centralized validation and error handling Scattered across multiple listeners
Type Safety Full TypeScript support with compile-time checking Limited type safety, runtime errors common
Testing Easy to mock and test individual components Complex setup with event mocking
Coupling Loose coupling through interfaces Tight coupling to event names and signatures

When to Use This Library

âś… Use Dependency Injection when:

  • You need to coordinate multiple services that depend on each other
  • You want to ensure services are initialized in the correct order
  • You need type-safe dependency resolution
  • You want to easily test and mock individual components
  • You need to handle complex initialization sequences

❌ Use Pub-Sub/Event Listeners when:

  • You need immediate, fire-and-forget notifications
  • You want to decouple completely unrelated components
  • You need one-to-many communication patterns
  • You're building real-time systems with immediate responses

Example: Dependency Injection vs. Event Listeners

Event Listeners (Immediate Execution):

// Event listeners fire immediately, even if dependencies aren't ready
eventBus.on('userCreated', (user) => {
  // This might fail if database isn't connected yet
  database.saveUser(user);
});

eventBus.on('userCreated', (user) => {
  // This might fail if email service isn't configured
  emailService.sendWelcomeEmail(user);
});

// Fires immediately - listeners might fail
eventBus.emit('userCreated', user);

Dependency Injection (Coordinated Execution):

// Dependencies are resolved in correct order
injector.resolve(['database', 'emailService'], (db, email) => {
  // Both services are guaranteed to be ready
  db.saveUser(user);
  email.sendWelcomeEmail(user);
});

// Register dependencies when they're actually ready
injector.register('database', databaseConnection);
injector.register('emailService', emailService);

Features

  • âś… Full TypeScript Support - Complete type definitions and IntelliSense
  • âś… Modern Node.js - Requires Node.js 20+ with latest features
  • âś… Asynchronous Resolution - Lazy dependency resolution
  • âś… High Test Coverage - 97.95% overall coverage, 100% source code coverage
  • âś… Zero Dependencies - Only uses lodash for utility functions
  • âś… Lightweight - Minimal overhead and bundle size

Requirements

  • Node.js 20.0.0 or higher
  • TypeScript 5.0 or higher (for development)

Installation

npm install --save minjex

Quick Start

JavaScript

const DependencyInjector = require('minjex');

const injector = new DependencyInjector();

// Register dependencies
injector.register('database', { host: 'localhost', port: 5432 });
injector.register('apiKey', 'your-api-key-here');

// Resolve dependencies
injector.resolve(['database', 'apiKey'], (db, key) => {
  console.log('Database:', db);
  console.log('API Key:', key);
  // Your application logic here
});

TypeScript

import DependencyInjector, { DependencyInjectionOptions } from 'minjex';

interface DatabaseConfig {
  host: string;
  port: number;
}

const injector = new DependencyInjector();

// Register dependencies with type safety
injector.register('database', { host: 'localhost', port: 5432 } as DatabaseConfig);
injector.register('apiKey', 'your-api-key-here');

// Resolve dependencies with full type support
injector.resolve(['database', 'apiKey'], (db: DatabaseConfig, key: string) => {
  console.log('Database:', db);
  console.log('API Key:', key);
});

API Reference

Constructor

new DependencyInjector(options?: DependencyInjectionOptions)

Options:

  • debug?: boolean - Enable debug logging (default: false)
  • dependencies?: Record<string, any> - Pre-register dependencies
  • dependants?: Record<string, Resolver[]> - Pre-register dependants

Methods

register(dependencyKey: string, dependencyValue: any): void

Registers a dependency that can be resolved later.

injector.register('service', new MyService());
injector.register('config', { timeout: 5000 });

resolve(dependencies: string[], resolver: ResolverFunction): void

Resolves dependencies and invokes the resolver function when all dependencies are available.

injector.resolve(['service', 'config'], (service, config) => {
  // This function is called when both 'service' and 'config' are available
  service.initialize(config);
});

inject(dependencies: string[]): void

Manually triggers dependency injection for the specified dependencies.

Advanced Usage

Lazy Resolution

Dependencies can be registered after resolvers are defined:

// Define resolver first
injector.resolve(['userService'], (userService) => {
  console.log('User service is ready!');
});

// Register dependency later
setTimeout(() => {
  injector.register('userService', new UserService());
}, 1000);

Context Access

Access dependencies through the this context:

injector.resolve(['db', 'cache'], function() {
  const database = this.db;
  const cache = this.cache;
  
  // Use dependencies
  database.query('SELECT * FROM users');
});

Dependency Change Detection

Resolvers are automatically re-invoked when dependencies change:

injector.resolve(['config'], (config) => {
  console.log('Config updated:', config);
});

injector.register('config', { version: '1.0' }); // Resolver called
injector.register('config', { version: '1.0' }); // Not called (same value)
injector.register('config', { version: '2.0' }); // Resolver called again

Debug Mode

Enable debug logging to track dependency resolution:

const injector = new DependencyInjector({ debug: true });

injector.register('test', 'value');
// Output: [DependencyInjection] register [Arguments] { '0': 'test', '1': 'value' }
// Output: [DependencyInjection] inject [ 'test' ]

Development

Prerequisites

  • Node.js 20+
  • npm or yarn

Setup

git clone https://github.com/serendipious/node-dependency-injection.git
cd node-dependency-injection
npm install

Building

npm run build

Testing

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test -- --reporter=html

Code Quality

The project maintains high code quality standards:

  • TypeScript with strict type checking
  • Pre-commit hooks that run tests and coverage checks
  • 97.95% test coverage with comprehensive test suite
  • Automated quality gates prevent commits that don't meet standards

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

License

BSD License - see LICENSE file for details.

Changelog

v0.5

  • Initial release as minjex
  • TypeScript migration from CoffeeScript
  • Node.js 20+ support
  • Comprehensive test suite (97.95% coverage)
  • Full type definitions
  • Pre-commit hooks for quality assurance
  • Modern documentation and contribution guidelines

About

Dependency Injection for Node.js

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published