npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

apex-di

v1.0.0

Published

Apex: Dependency Injection Component ====================================

Readme

Apex: Dependency Injection Component

This package adds the inversion of control (IoC) code pattern to your (node) project.

Installation

Using yarn

$ yarn add apex-di

or npm

$ npm install apex-di --save

Getting started

Start by creating the container. A YAML file loader is available to load container configuration.

const DI        = require('apex-di'),
      container = new DI.Container(),
      loader    = new DI.YamlLoader();

// Load parameters.yml into the service container.
container.load(loader, __dirname + '/config/parameters.yml');
# config/parameters.yml
parameters:
    foobar: "Hello World!"
    title: "This is %foobar%!"

Now that we have parameters.yml loaded into the container, we can fetch these parameters like so:

let foobar = container.getParameter('foobar'); // Hello World!
let title  = container.getParameter('title'); // This is Hello World!!

Working with services

A service is basically an instantiated javascript function (or class) that is reused throughout the lifetime of the application. The idea of a service is that it is initialized once and reused later.

Lets say we have a class called MathService that we want to have injected somwhere.

class MathService
{
    add (a, b)
    {
        return a + b;
    }
}

module.exports = MathService;

Without the use of dependency injection, you would just do something like this:

const MathService = require('./MathService');

class MyService
{
    constructor ()
    {
        this.math_service = new MathService();
    }
}

module.exports = MyService;

However, if you want to unit-test MyService, there is no easy way to mock away the instance of math_service without extending MyService and overriding the constructor or the math_service property during instantiation.

Dependency injection solves this for you.

If we have the following yaml file:

services:
    my_service:
        class: !require "./MyService"
        arguments:
            - "@math_service"

    math_service:
        class: !require "./MathService"

A service is referenced using the @-prefix in your yaml file.

MyService now looks like this:

class MyService
{
   constructor (math_service)
   {
       this.math_service = math_service;
   }
   
   add (a, b)
   {
       return this.math_service.add(a, b);
   }
}

module.exports = MyService;

The dependency on MathService is no longer there and can be interchanged with anything else, as long as it complies to the same signatures as the original service.

Simply fetch the service from the container using the get() function:

let my_service = container.get('my_service');
my_service.add(2, 3); // 5

Once the first service is fetched from the container, the container is compiled. It will resolve references to other services and initialize them when needed. This effectively means that everything is lazy loaded.

Features

Importing other YAML files

You can import other yaml files from the current one, using the following syntax:

imports:
    - "relative/to/current.yml"
    - "relative/another.yml"

The path is always relative to the currently parsed yaml file.

Compiler passes

Compiler passes allow for modifications to the container during compilation. A compiler pass is effectively a function (or class) that will be instaniated that has a compile method that accepts the container as its first and only argument.

A typical pass would look like this:

const Reference = require('apex-di').Reference;

module.exports = class MyCompilerPass
{
    compile (container)
    {
        let my_definition   = container.getDefinition('my_service_definition');
        let tagged_services = container.findTaggedServiceIds('some_tag'),
            references      = [];
        
        tagged_services.forEach((id) => {
            references.push(new Reference(id));
        });
        
        my_definition.replaceArgument(0, references);
    }
}

Add the compiler pass either in javascript or a yaml file.

container.addCompilerPass(new MyCompilerPass());
# container.yml
passes:
    - !require "./MyCompilerPass"

The example CompilerPass as it is given above can be simplified drastically by using the !tagged YAML type as described below.

Auto injecting tagged services

You can auto-inject tagged services into another service definition.

services:
    my_service_collector:
        class: !require "./my_module"
        arguments:
            - !tagged "my.tag"
            
    some_service_1:
        class: !require "./services/some_service_1"
        tags:  ["my.tag"]

    some_service_2:
        class: !require "./services/some_service_2"
        tags:  ["my.tag"]

In the example above, my_service_collector will have both some_service_1 and some_service_2 injected as an array in its first argument.

This is the equivalent of;

services:
    my_service_collector:
        class: !require "./my_module"
        arguments:
            - ["@some_service_1", "@some_service_2"]

... but injected services are not "hard-coded".

The advantage of this is pluggability. If a container is shared across an application and configuration from specific node modules is loaded into the same container, this can enable 'third-party' instances to be injected into services, which is effectively a basic form of "plugin management".

Require a specific class from a module

You can require a specific class from a node module using the following require syntax inside a yaml file:

services:
    my_service:
        class: !require ["./MyFrameworkThing", "MyService"]

This is useful for node modules that export multiple functions that could effectively be used as services, for example something like this:

module.exports = {
    MyService: require('./MyService') // exports a function that can be instantiated.
}

Don't want to use YAML configuration?

Well, then you're out of lu... just kidding.

You can build the entire cntainer manually by using the Definition and Reference classes. Look at the source of each of those files to see which public methods are available to work with. It's not all that complicated.

const DI        = require('apex-di'),
      container = new DI.Container();

// Add a definition.
container.setDefinition('my_service', new Definition(require('./MyService'), ['arg1', 2, {arg: 3}]));

// Compile (and freeze) the container.
container.compile();

// Fetch a service.
container.get('my_service'); // Instance of whatever is exported from ./MyService.js

Referencing other services is as easy as the following:

let my_service_definition = new Definition(require('./MyService')),
    another_definition    = new Definition(require('./AnotherService'));

container.setDefinition('my_service', my_service_definition);
container.setDefinition('another_service', another_definition);

my_service_definition.setArguments([ new Reference('another_service') ]);