Skip to content

A tiny, dead simple context & dependency manager for node-js applications

License

Notifications You must be signed in to change notification settings

naikus/app-context

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

app-context

A tiny, dead simple context & dependency container for node-js applications. Now checks for missing modules and cyclic dependencies

Usage

In your package.json, add it as a dependency

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "My Application",
  "main": "index.js"
  // ...
  "dependencies": {
    // ...other deps
    "app-context": "github:naikus/app-context#error-reporting"
  }
}

Define a module/service or multiple services (in their own files)

// File users/index.js
const createUserService = require("./service");
/**
 * @type {ModuleDefn}
 */
module.exports = {
  name: "users",
  /**
   * Initializes the users module
   * @param {AppContext} context The application context
   */
  async initialize(context) {
    // "api-router" & "persistence" are modules that are registered with the app context elsewhere
    const [apiRouter, db] = await context.dependency(["api-router", "db"]),
        service = createUserService(db);

    // Alternatively you can also use the following syntax
    context.dependency(["api-router", "db"], (apiRouter, db) => {
      // Do something
    });

    // Get notified for events from context or other modules
    const unsub = context.on("some:event", data => {
      // do something
    });

    // Emit events
    context.emit("users:add", {name: "Alice"});
    
    // Whatever you return here will me made available via context.getModule(name) which is safe
    // to call after context has started
    // const userService = context.getModule("users");
    return service;
  }
};

Then in your main file, wire all the modules. The order is not important, app-context will instantiate your modules in the right order.

const apiRouter = require("./api-router"),
  db = require("./db"),
  users = require("./users");

const AppContext = require("app-context"),
  context = AppContext.create();

// ....

// Later in your main start function
context.register(users)
  .register(apiRouter)
  .register(db)
  // You can also register inline modules
  .register({
    name: "config",
    initialize: () => ({
      property: "value"
    })
  });

try {    
  await context.start();
  logger.info("Application started 🚀");
  context.emit("app:initialize");
}catch(error) {
  logger.error("Application failed to start", error);
  process.exit(1);
};

How to get around cyclic dependencies

If your modules depend on one another, i.e. cyclic dependencies, you can get around by registering an event listener on app-context

  const context = AppContex.create();
  context.register({
    name: "module_a",
    async initialize(ctx) {
      const [moduleB] = await ctx.dependency("module_b");
      // Do something with moduleB...
      moduleB.sayHello();
  
      // Return the actual module
      return {
        sayHello() {
          console.log("Hello from module_a");
        }
      };
    }
  });
  
  context.register({
    name: "module_b",
    async initialize(ctx) {
      // This will throw error (cyclic dependencies)
      // const [moduleA] = await ctx.dependency("module_a");

      // Instead do this:
      ctx.once("module:module_a", moduleA => {
        // module_a is now available
      });
  
      // Return the actual module
      return {
        sayHello() {
          console.log("Hello from module_b");
        }
      };
    }
  });
  context.start();

About

A tiny, dead simple context & dependency manager for node-js applications

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published