{"id":19166682,"url":"https://github.com/jiripospisil/ashley","last_synced_at":"2025-07-18T08:06:50.937Z","repository":{"id":57184784,"uuid":"71660241","full_name":"jiripospisil/ashley","owner":"jiripospisil","description":"Ashley is a dependency injection container for JavaScript.","archived":false,"fork":false,"pushed_at":"2018-07-06T18:42:28.000Z","size":76,"stargazers_count":24,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-07-11T03:23:37.820Z","etag":null,"topics":["dependency-injection","ioc","ioc-container","javascript","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jiripospisil.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-22T19:15:05.000Z","updated_at":"2023-05-31T05:52:33.000Z","dependencies_parsed_at":"2022-09-14T09:01:09.781Z","dependency_job_id":null,"html_url":"https://github.com/jiripospisil/ashley","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/jiripospisil/ashley","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiripospisil%2Fashley","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiripospisil%2Fashley/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiripospisil%2Fashley/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiripospisil%2Fashley/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jiripospisil","download_url":"https://codeload.github.com/jiripospisil/ashley/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiripospisil%2Fashley/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265725160,"owners_count":23817971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","ioc","ioc-container","javascript","nodejs"],"created_at":"2024-11-09T09:33:58.500Z","updated_at":"2025-07-18T08:06:50.914Z","avatar_url":"https://github.com/jiripospisil.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ashley\n\nAshley is a dependency injection container for JavaScript. Learn more about\n[dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) or\nmore generally about [inversion of\ncontrol](https://en.wikipedia.org/wiki/Inversion_of_control) on Wikipedia.\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Binding instances](#binding-instances)\n  - [Binding plain objects](#binding-plain-objects)\n  - [Binding functions](#binding-functions)\n  - [Factories](#factories)\n  - [Scopes](#scopes)\n  - [Container hierarchies](#container-hierarchies)\n- [Integration with existing frameworks and libraries](#integration-with-existing-frameworks-and-libraries)\n- [Recommendations](#recommendations)\n- [FAQ](#faq)\n- [License](#license)\n\n# Installation\n\n```bash\nnpm install ashley\n```\n\nNote that it makes a heavy use of async functions and thus requires a fairly\nrecent version of Node.js (7.x or newer). Depending on the version, you might\nneed to pass the `--harmony-async-await` flag.\n\n# Usage\n\nA new instance of Ashley can be created simply by calling its\nconstructor. Ashley instances do not share any state and there can be\nany number of them within the same application. If fact, it is\nsometimes beneficial to have more than one them as they can form\nhierarchies. More on that later.\n\n```javascript\nconst Ashley = require('ashley');\nconst ashley = new Ashley();\n```\n\nNote that the code samples will use the container directly for obtaining the\nconfigured objects. To take advantage of the dependency injection pattern in a\nreal application, the container should be only used explicitly during the\napplication's initialization process to set up the dependencies. Read more in\nthe [Recommendations](#recommendations) section.\n\n## Binding instances\n\nThe most basic thing Ashley can bind is an instance of a class. A class in this\ncontext is anything that needs to be instantiated with the `new` operator.\n\n```javascript\nashley.instance('Logger', ConsoleLogger);\nconst logger = await ashley.resolve('Logger')\n\n// the same as\nconst logger = new ConsoleLogger();\n```\n\nThe first argument of the `instance` method is a name. This name can be used\nwhen resolving instances or declaring dependencies. The second argument can\neither be the class itself or a path to a file which defines it. Finally, the\nthird argument is a list of dependencies.\n\n```javascript\nashley.instance('Logger', ConsoleLogger);\nashley.instance('OrderService', OrderService, ['Logger']);\nconst orderService = await ashley.resolve('OrderService')\n\n// the same as\nashley.instance('Logger', 'src/console_logger');\nashley.instance('OrderService', 'src/order_service', ['Logger']);\nconst orderService = await ashley.resolve('OrderService')\n\n// the same as\nconst logger = new ConsoleLogger();\nconst orderService = new Orderservice(logger);\n```\n\nNote that when a relative path is provided, Ashley needs to know the\nroot path from which the relative paths should be resolved.\n\n```javascript\nconst ashley = new Ashley({\n  root: __dirname\n});\n```\n\nThere are objects within all applications which are meant to be used\nas singletons but making them actual\n[singletons](https://en.wikipedia.org/wiki/Singleton_pattern) is\n[problematic](https://code.google.com/archive/p/google-singleton-detector/wikis/WhySingletonsAreControversial.wiki).\n\nAn alternative is to write regular classes but let Ashley worry about their life\ntime (scope) once they are instantiated. Ashley provides two scopes out of the\nbox - `Singleton` and `Prototype`. The `Singleton` scope is used by default and\nwill make Ashley to always return the same instance each time it's requested.\nThe `Prototype` scope, on the other hand, will make Ashley to always create new\ninstances when requested.\n\n```javascript\nashley.instance('DbConnection', 'src/rethink_db_connection', [], {\n  scope: 'Singleton' // default\n});\n\nashley.instance('TimePoint', 'src/time_point', [], {\n  scope: 'Prototype'\n});\n```\n\nFor an object such as `DbConnection` to be useful, it needs to actually\nestablish an connection which will most likely be an asynchronous process. When\nbinding the object, it's possible to specify that an initialization method needs\nto be called for the object to be fully ready. This is similar to a constructor\nbut allows the method to be asynchronous.\n\n```javascript\nashley.instance('DbConnection', 'src/rethink_db_connection', [], {\n  initialize: true\n});\n```\n\nWhen set to `true`, Ashley will look for an async method called `initialize`\nand will wait for it to finish before proceeding. It's possible to specify a\ndifferent initialize method by setting `initialize` to the name.\n\n```javascript\nclass RethinkDbConnection {\n  async init() {\n    this.connection = await r.connect();\n  }\n}\n\nashley.instance('DbConnection', RethinkDbConnection, [], {\n  initialize: 'init'\n});\n```\n\nThe `initialize` method should either succeed or throw an error. It's important\nto make sure that time outs are set and handled as well otherwise Ashley might\nwait indefinitely.\n\nThe initialization method is the same for all instances of the object. Ashley\nalso provides a way to set up a specific instance by defining an `setup`\nfunction when binding the instance.\n\n```javascript\nashley.instance('ErrorLogger', ConsoleLogger, [], {\n  setup: function(logger) {\n    logger.setBold(true);\n    logger.setColor(ConsoleLogger.COLOR_RED);\n  }\n});\n\nashley.instance('Logger', ConsoleLogger);\n```\n\nThe `setup` function will receive the instantiated object as its only\nparameter. Note that the function will be called only once if the scope is set\nto `Singleton` and may or may not be an async function.\n\nThere's also the option to `deinitialize` instances which works the same way\nexcept it's invoked when the container is being shut down. It generally\ndepends on the scope used whether the method is supported or when it's\ncalled.\n\nThe provided `Singleton` and `Prototype` scopes will call the method only when\nthe container is being shutdown. This means that the individual instances need\nto be kept in memory until that happens. Consider calling the `deinitialize`\nmethod manually on the objects in case they are short lived and keeping them in\nmemory until the container shuts down is problematic.\n\n```javascript\nclass RethinkDbConnection {\n  async initialize() {\n    this.connection = await r.connect();\n  }\n\n  async deinitialize() {\n    if (this.connection) {\n      await this.connection.close();\n    }\n  }\n}\n\nashley.instance('DbConnection', RethinkDbConnection, [], {\n  initialize: true,\n  deinitialize: true\n});\n\n// ...\n\nawait ashley.shutdown();\n```\n\nNote that Ashley does NOT catch errors from these methods. It's up to the\ndeveloper to handle the failure scenarios themselves. It's especially important\nfor the `deinitialize` method as throwing within the method will halt\nde-initialization of the remaining binds.\n\n## Binding plain objects\n\nNot everything needs to be wrapped in a class and sometimes it's convenient to\nbind just plain objects.\n\n```javascript\nashley.object('Config', { port: 9001 });\nashley.object('Title', 'Zoo');\n\nconst title = await ashley.resolve('Title');\n```\n\nBy default the bound objects are passed by reference and thus everyone will\nreceive the very same object and can possibly modified it. An alternative is to\nspecify the `clone` option which will create a deep copy of the object each time\nit's requested.\n\n```javascript\nashley.object('FreshConfig', { port: 9001 }, {\n  clone: true // uses https://lodash.com/docs/4.15.0#cloneDeep\n});\n```\n\nSince some objects require special care when deep copying, it's possible to\nspecify the function that should be used for the purpose.\n\n```javascript\nashley.object('Config', { port: 9001 }, {\n  clone: function(obj) {\n    // ...\n    return copy;\n  }\n});\n```\n\nNote that you cannot specify the target using a file path since a string is also\na valid target in itself.\n\n```javascript\nashley.object('Config', 'src/config');\nawait ashley.resolve('Config'); // =\u003e 'src/config'\n```\n\n## Binding functions\n\nWhen integrating with 3rd party frameworks or libraries, it's sometimes\nnecessary to register callbacks which will later be invoked with a given set of\nparameters. For example when using [Koa](http://koajs.com).\n\n```javascript\nconst Koa = require('koa');\nconst app = new Koa();\n\nconst ConsoleLogger = require('src/console_logger');\nconst logger = new ConsoleLogger();\n\napp.use(async function Index(ctx, next) {\n  logger.info(`Serving ${ctx.request.ip}`);\n  ctx.body = 'Hello world';\n});\n\n// or\napp.use(require('src/index'));\n```\n\nThe goal here is to invoke the callback with not only the parameters provided by\nKoa (`ctx` and `next`) but also with configured dependencies, in this case an instance of\n`ConsoleLogger`. It's possible to take advantage of the `function` method as\nfollows.\n\n```javascript\nashley.instance('Logger', 'src/console_logger');\nashley.function('Index', 'src/index', [Ashley._, Ashley._, 'Logger']);\n\napp.use(await ashley.resolve('Index'));\n```\n\nDefining a `function` and passing it immediately afterwards is a very common\npattern and can be shortened to just a single line. It takes advantage of the\nfact that binding a target returns an async function which resolves to the\ntarget.\n\n```javascript\napp.use(await ashley.function('Index', 'src/index', [Ashley._, Ashley._, 'Logger']));\n```\n\nNotice the use of the `Ashley._` placeholder. When present, it will be replaced\nwith the parameter the callback was called with by the framework. In addition,\nAshley will resolve the dependencies and pass all of it to the user defined\nfunction.\n\n```javascript\n// src/index\nmodule.exports = async function Index(ctx, next, logger) {\n  logger.info(`Serving ${ctx.request.ip}`);\n  ctx.body = 'Hello world';\n}\n```\n\nIt's possible to use the placeholder multiple times and in any order. If the\ncallback is called with fewer parameters than expected, the remaining\nplaceholders are passed in as `undefined`, followed by the declared\ndependencies. When the number of parameters is greater than expected, only those\nwith a placeholder will be passed in. Note that the returned function will\nalways be an async function.\n\nThe Koa framework is officially supported. See [Integration with existing\nframeworks and libraries](#integration-with-existing-frameworks-and-libraries)\nfor more information.\n\n## Factories\n\nCreating instances of classes or other objects is not always as straightforward\nas calling its constructor, especially when using 3rd party libraries. Factories\ngive the option to fully control the process. Note that a factory always needs\nto return a new instance.\n\n```javascript\n\n// src/console_logger\nmodule.exports = function consoleLoggerFactory(config) {\n  const logger = ...\n  // ..\n  return logger;\n};\n\n// Define the factory\nashley.factory('ConsoleLogger', 'src/console_logger_factory', ['Config']);\n\n// Use the factory and specify other details such as the life time\nashley.link('Logger', 'ConsoleLogger', {\n  scope: 'Singleton'\n});\n\n// Use the `Logger` as a dependency\nashley.instance('Service', 'src/service', ['Logger']);\n```\n\nNotice the use of the `link` method. It's used to tie the factory with a name\nand gives the option to specify details such as the life time. There can be any\nnumber of links for a particular factory.\n\n## Scopes\n\nScopes define the life time of the objects they manage. As already mentioned,\nAshley provides two scopes out of the box - `Singleton` and `Prototype`. The\n`Singleton` scope will make sure to always return the same instance while\n`Prototype` will always create a new instance.\n\n```javascript\nashley.instance('Logger', 'src/console_logger', [], {\n  scope: 'Singleton'\n});\n```\n\nIt's of course possible to create custom scopes. Internally, a scope is nothing\nmore than a class that gets instantiated with a provider which knowns how to\ncreate a new instance of the managed object. Later, the scope is asked for an\ninstance of the object and it's up the implementation to decide whether to\ncreate a new one (e.g. `Prototype`) or always return the same (e.g.\n`Singleton`).\n\n## Container hierarchies\n\nAs applications grow, it's often desirable to split them into independent\nmodules each handling their own agenda and making them communicate via shared\nmeans.\n\nTo help in this scenario, Ashley containers can form hierarchies. Each module\ncan have its container linked to the same parent container. When set up, each\nrequest for an unmet dependency will bubble up the hierarchy until found.\n\n```javascript\n// core\nconst core = new Ashley();\nashley.instance('MessageBus', 'src/message_bus');\n\n// module A\nconst moduleA = core.createChild();\nmoduleA.instance('ServiceA', 'src/service_a', ['MessageBus']);\n\n// module B\nconst moduleB = core.createChild();\nmoduleB.instance('ServiceB', 'src/service_b', ['MessageBus']);\n```\n\nThe way a parent container is passed to its children various from application to\napplication and there's no universal way. Note however that it's possible to\ninject the current container as a dependency. In the following sample,\n`ModuleInitializer` will receive a reference to the `ashley` variable.\n\n```javascript\nashley.instance('ModuleInitializer', ['@containers/self']);\n```\n\nIn rare cases, you can even inject the container's parent. Use with caution as\nit creates often unwanted dependency between the containers.\n\n```javascript\nashley.instance('ModuleA', ['@containers/parent']);\n```\n\nHierarchies are also often useful for creating temporary containers. For\nexample, a framework might want to provide a way of having Singleton instances\nbut only for the duration of a web request. To do that, a child container is\ncreated for every request with a reference to the main application's container.\nThis way, the main container can resolve dependencies which outlive the request\nbut still have the possibility to have per request dependencies such as an\nobject for holding the current user and others.\n\n```javascript\n// Initialized with the application\nconst main = new Ashley();\nmain.instance('Logger', 'src/console_logger');\n\n// ...\n// Created for every request and bound to the request's context\nconst request = main.createChild();\nrequest.function('Index', 'src/middlewares/index', [Ashley._, Ashley._, 'Logger']);\n\n// ...\n\n// Shutdown the container at the end\nawait request.shutdown();\n```\n\n# Integration with existing frameworks and libraries\n\nIntegration with existing frameworks or libraries usually requires glue between\nits components and Ashley itself. The support for these integrations goes into\nseparate packages prefixed with \"ashley-\".\n\nThe `Koa` web framework is officially supported and extracted into its own\npackage [ashley-koa](https://github.com/jiripospisil/ashley-koa). Head over\nthere for more information and usage examples.\n\n# Recommendations\n\n## Objects should declare all of the their dependencies\n\nIndividual objects in the system should declare all of their dependencies and\nhave them injected. They should not depend on or modify any global state. This\nmakes it easy to work with them in isolation (e.g. in unit tests).\n\n## Objects should not depend on the container\n\nIndividual objects should not depend on the presence of the container. They\nshould work the same even if the container was completely removed and the\ndependencies set up manually.\n\n## Do not use or inject the container outside of an initialization phase\n\nThe container should be explicitly referenced only during an initialization\nphase to set up the dependencies. As soon as the application is initialized, it\nshould not need the container directly anymore.\n\nIf the application is split into modules and it's necessary to pass the\ncontainer down the chain to register it as a parent, it's possible to inject it\nas follows.\n\n```javascript\nashley.instance('ModuleInitializer', ['@containers/self']);\n```\n\n## Create new instances directly or with factories\n\nWhen it's necessary to create a new instance of a class and provide its\ndependencies, the dependencies should be available / injected in the current\nscope already. Another approach is to let the container inject a user-defined\nfactory which can create new instances on its own.\n\nIt's also possible to use the internally factory for the object and let the\ncontainer inject it. This is very similar to the factory pattern mentioned\nbefore but makes the container do the work.\n\n```javascript\nashley.instance('Item', 'src/item', ['Dependency1', 'Dependency2']);\nashley.instance('Service', 'src/service', ['@factories/Item']);\n\n// src/item\nclass Item {\n  constructor(dependency1, dependency2) {\n    // ...\n  }\n}\n\n// src/service\nclass Service {\n  constructor(itemFactory) {\n    this.itemFactory = itemFactory;\n  }\n\n  async action() {\n    const item1 = await this.itemFactory.create();\n    const item2 = await this.itemFactory.create();\n    // ...\n  }\n}\n```\n\n## Use constants as names\n\nWhen defining a large number of bindings, it's easy to loose track of where a\nparticular dependency is used. One way to alleviate the problem is to use\nconstants instead of string names. Most editors/IDEs will highlight usages of\nthese and it will also allow for precise name refactorings.\n\n```javascript\nconst c = {\n  Service: 'Service',\n  Logger: 'Logger'\n};\n\nashley.instance(c.Logger, 'src/console_logger');\nashley.instance(c.Service, 'src/service', [c.Logger]);\n```\n\n## Do not inject null / undefined\n\nSome dependencies are not required in all environments or installations. The\npreferred way of dealing with such scenario is to bind a dummy implementation\nwhich offers the same interface but does not actually do anything. This way the\ncode doesn't have to account for the possibility of the dependency being absent\nand clutter the code with conditions.\n\n# FAQ\n\n## Is it possible to set up dependencies using annotations?\n\nNo, there's no built in support for doing that. Additionally to the fact that\nthe library would then need to use a transpiler (since annotations are not yet\nofficially supported in the language), there are a few more reasons:\n\n- Annotations would create a hard dependency on the container itself. Ideally\n  the dependencies should not know about that fact that they're being used with\n  a container.\n\n- It would be difficult to annotate an object coming from a 3rd party library\n  since these cannot / should not be modified.\n\n- There would be no single place to see the whole dependency graph in one place.\n  Instead, the graph would be spread thorough the code base making it difficult\n  to make sense of it.\n\n## Should I use Ashley for projects of any size?\n\nNo, use Ashley or other dependency injection containers only when there's\nbenefit. Don't use a container just for the sake of using a container. For tiny\napplications, it's often overkill and setting up the dependencies manually is\npreferable. Note that using dependency injection alone is beneficial for\nprojects of any size.\n\n# License\n\nICS\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiripospisil%2Fashley","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjiripospisil%2Fashley","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiripospisil%2Fashley/lists"}