{"id":13787200,"url":"https://github.com/adonisjs/fold","last_synced_at":"2026-01-06T05:15:58.436Z","repository":{"id":43497022,"uuid":"44580355","full_name":"adonisjs/fold","owner":"adonisjs","description":"IoC container with all required goodies to write flexible node applications","archived":false,"fork":false,"pushed_at":"2025-04-25T05:15:28.000Z","size":1870,"stargazers_count":130,"open_issues_count":0,"forks_count":21,"subscribers_count":10,"default_branch":"10.x","last_synced_at":"2025-05-07T23:02:17.450Z","etag":null,"topics":["bundled-with-core","ioc-container"],"latest_commit_sha":null,"homepage":"https://docs.adonisjs.com/guides/ioc-container","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adonisjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"thetutlage"}},"created_at":"2015-10-20T03:41:02.000Z","updated_at":"2025-05-02T17:25:20.000Z","dependencies_parsed_at":"2024-01-05T07:30:55.848Z","dependency_job_id":"0799aa64-de52-4d51-8f1e-199b19170c9e","html_url":"https://github.com/adonisjs/fold","commit_stats":{"total_commits":457,"total_committers":8,"mean_commits":57.125,"dds":"0.10722100656455147","last_synced_commit":"58cc87e14092646f871880dfc2fe580c0e1885bc"},"previous_names":["poppinss/adonis-fold"],"tags_count":106,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Ffold","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Ffold/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Ffold/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Ffold/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adonisjs","download_url":"https://codeload.github.com/adonisjs/fold/tar.gz/refs/heads/10.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254245432,"owners_count":22038457,"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":["bundled-with-core","ioc-container"],"created_at":"2024-08-03T20:00:31.278Z","updated_at":"2026-01-06T05:15:58.363Z","avatar_url":"https://github.com/adonisjs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/thetutlage"],"categories":["Optimization"],"sub_categories":[],"readme":"# AdonisJS Fold\n\n\u003e Simplest and straightforward implementation of IoC container in JavaScript\n\n\u003cbr /\u003e\n\n[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url]\n\n## Why this project exists?\n\nMany existing implementations of IoC containers take the concept too far and start to feel more like Java. JavaScript inherently does not have all the bells and whistles; you need to have similar IoC container benefits as PHP or Java.\n\nTherefore, with this project, I live to the ethos of JavaScript and yet build a container that can help you create loosely coupled systems.\n\nI have explained the [reasons for using an IoC container](https://github.com/thetutlage/meta/discussions/4) in this post. It might be a great idea to read the post first ✌️\n\n\u003e **Note**: AdonisJS fold is highly inspired by the Laravel IoC container. Thanks to Taylor for imaginging such a simple, yet powerful API.\n\n## Goals of the project\n\n- **Keep the code visually pleasing**. If you have used any other implementation of an IoC container, you will automatically find `@adonisjs/fold` easy to read and follow.\n- **Keep it simple**. JavaScript projects have a few reasons for using an IoC container, so do not build features that no one will ever use or understand.\n- **Build it for JavaScript and improve with TypeScript** - The implementation of `@adonisjs/fold` works with vanilla JavaScript. It's just you have to write less code when using TypeScript. Thanks to its decorators metadata API.\n\n## Usage\n\nInstall the package from the npm packages registry.\n\n```sh\nnpm i @adonisjs/fold\n```\n\nOnce done, you can import the `Container` class from the package and create an instance of it. For the most part, you will use a single instance of the container.\n\n```ts\nimport { Container } from '@adonisjs/fold'\n\nconst container = new Container()\n```\n\n## Making classes\n\nYou can construct an instance of a class by calling the `container.make` method. The method is asynchronous since it allows for lazy loading dependencies via factory functions (More on factory functions later).\n\n```ts\nclass UserService {}\n\nconst service = await container.make(UserService)\nassert(service instanceof UserService)\n```\n\nIn the previous example, the `UserService` did not have any dependencies; therefore, it was straightforward for the container to make an instance of it.\n\nNow, let's look at an example where the `UserService` needs an instance of the Database class.\n\n```ts\nclass Database {}\n\nclass UserService {\n  static containerInjections = {\n    _constructor: {\n      dependencies: [Database],\n    },\n  }\n\n  constructor(db) {\n    this.db = db\n  }\n}\n\nconst service = await container.make(UserService)\nassert(service.db instanceof Database)\n```\n\nThe `static containerInjections` property is required by the container to know which values to inject when creating an instance of the class.\n\nThis property can define the dependencies for the class methods (including the constructor). The dependencies are defined as an array. The dependencies are injected in the same order as they are defined inside the array.\n\n\u003e **Do you remember?** I said that JavaScript is not as powerful as Java or PHP. This is a classic example of that. In other languages, you can use reflection to look up the classes to inject, whereas, in JavaScript, you have to tell the container explicitly.\n\n### TypeScript to the rescue\n\nWait, you can use decorators with combination of TypeScript's [emitDecoratorMetaData](https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata) option to perform reflection. You will also need to install [`reflect-metadata`](https://www.npmjs.com/package/reflect-metadata) in order for TypeScript to extract metadata from your classes.\n\nIt is worth noting, TypeScript decorators are not as powerful as the reflection API in other languages. For example, in PHP, you can use interfaces for reflection. Whereas in TypeScript, you cannot.\n\nWith that said, let's look at the previous example, but in TypeScript this time.\n\n```ts\nimport { inject } from '@adonisjs/fold'\n\nclass Database {}\n\n@inject()\nclass UserService {\n  constructor(db: Database) {\n    this.db = db\n  }\n}\n\nconst service = await container.make(UserService)\nassert(service.db instanceof Database)\n```\n\nThe `@inject` decorator looks at the types of all the constructor parameters and defines the `static containerInjections` property behind the scenes.\n\n\u003e **Note**: The decorator-based reflection can only work with concrete values, not with interfaces or types since they are removed during the runtime.\n\n## Making class with runtime values\n\nWhen calling the `container.make` method, you can pass runtime values that take precedence over the `containerInjections` array.\n\nIn the following example, the `UserService` accepts an instance of the ongoing HTTP request as the 2nd param. Now, when making an instance of this class, you can pass that instance manually.\n\n```ts\nimport { inject } from '@adonisjs/fold'\nimport { Request } from '@adonisjs/core/src/Request'\n\nclass Database {}\n\n@inject()\nclass UserService {\n  constructor(db: Database, request: Request) {\n    this.db = db\n    this.request = request\n  }\n}\n```\n\n```ts\ncreateServer((req) =\u003e {\n  const runtimeValues = [undefined, req]\n\n  const service = await container.make(UserService, runtimeValues)\n  assert(service.request === req)\n})\n```\n\nIn the above example:\n\n- The container will create an instance of the `Database` class since it is set to `undefined` inside the runtime values array.\n- However, for the second position (ie `request`), the container will use the `req` value.\n\n## Calling methods\n\nYou can also call class methods to look up/inject dependencies automatically.\n\nIn the following example, the `UserService.find` method needs an instance of the Database class. The `container.call` method will look at the `containerInjections` property to find the values to inject.\n\n```ts\nclass Database {}\n\nclass UserService {\n  static containerInjections = {\n    find: {\n      dependencies: [Database],\n    },\n  }\n\n  async find(db) {\n    await db.select('*').from('users')\n  }\n}\n\nconst service = await container.make(UserService)\nawait container.call(service, 'find')\n```\n\nThe TypeScript projects can re-use the same `@inject` decorator.\n\n```ts\nclass Database {}\n\nclass UserService {\n  @inject()\n  async find(db: Database) {\n    await db.select('*').from('users')\n  }\n}\n\nconst service = await container.make(UserService)\nawait container.call(service, 'find')\n```\n\nThe **runtime values** are also supported with the `container.call` method.\n\n## Container bindings\n\nAlongside making class instances, you can also register bindings inside the container. Bindings are simple key-value pairs.\n\n- The key can either be a `string`, a `symbol` or a `class constructor`.\n- The value is a factory function invoked when someone resolves the binding from the container.\n\n```ts\nconst container = new Container()\n\ncontainer.bind('db', () =\u003e {\n  return new Database()\n})\n\nconst db = await container.make('db')\nassert(db instanceof Database)\n```\n\nFollowing is an example of binding the class constructor to the container and self constructing an instance of it using the factory function.\n\n```ts\ncontainer.bind(Database, () =\u003e {\n  return new Database()\n})\n```\n\n### Factory function arguments\n\nThe factory receives the following three arguments.\n\n- The `resolver` reference. Resolver is something container uses under the hood to resolve dependencies. The same instance is passed to the factory, so that you can resolve dependencies to construct the class.\n- An optional array of runtime values defined during the `container.make` call.\n\n```ts\ncontainer.bind(Database, (resolver, runtimeValues) =\u003e {\n  return new Database()\n})\n```\n\n### When to use the factory functions?\n\nI am answering this question from a framework creator perspective. I never use the `@inject` decorator on my classes shipped as packages. Instead, I define their construction logic using factory functions and keep classes free from any knowledge of the container.\n\nSo, if you create packages for AdonisJS, I highly recommend using factory functions. Leave the `@inject` decorator for the end user.\n\n## Binding singletons\n\nYou can bind a singleton to the container using the `container.singleton` method. It is the same as the `container.bind` method, except the factory function is called only once, and the return value is cached forever.\n\n```ts\ncontainer.singleton(Database, () =\u003e {\n  return new Database()\n})\n```\n\n## Binding values\n\nAlong side the factory functions, you can also bind direct values to the container.\n\n```ts\ncontainer.bindValue('router', router)\n```\n\nThe values are given priority over the factory functions. So, if you register a value with the same name as the factory function binding, the value will be resolved from the container.\n\nThe values can also be registered at the resolver level. In the following example, the `Request` binding only exists for an isolated instance of the resolver and not for the entire container.\n\n```ts\nconst resolver = container.createResolver()\nresolver.bindValue(Request, req)\n\nawait resolve.make(SomeClass)\n```\n\n## Aliases\n\nContainer aliases allows defining aliases for an existing binding. The alias should be either a `string` or a `symbol`.\n\n```ts\ncontainer.singleton(Database, () =\u003e {\n  return new Database()\n})\n\ncontainer.alias('db', Database)\n\n/**\n * Make using the alias\n */\nconst db = await container.make('db')\nassert.instanceOf(db, Database)\n```\n\n## Contextual bindings\n\nContextual bindings allows you to register custom dependency resolvers on a given class for a specific dependency. You will be mostly using contextual bindings with driver based implementations.\n\nFor example: You have a `UserService` and a `BlogService` and both of them needs an instance of the Drive disk to write and read files. You want the `UserService` to use the local disk driver and `BlogService` to use the s3 disk driver.\n\n\u003e **Note**\n\u003e Contextual bindings can be defined for class constructors and not for container bindngs\n\n```ts\nimport { Disk } from '@adonisjs/core/driver'\n\nclass UserService {\n  constructor(disk: Disk) {}\n}\n```\n\n```ts\nimport { Disk } from '@adonisjs/core/driver'\n\nclass BlogService {\n  constructor(disk: Disk) {}\n}\n```\n\nNow, let's use contextual bindings to tell the container that when `UserService` needs the `Disk` class, provide it the local driver disk.\n\n```ts\ncontainer\n  .when(BlogService)\n  .asksFor(Disk)\n  .provide(() =\u003e drive.use('s3'))\n\ncontainer\n  .when(UserService)\n  .asksFor(Disk)\n  .provide(() =\u003e drive.use('local'))\n```\n\n## Swapping implementations\n\nWhen using the container to resolve a tree of dependencies, quite often you will have no control over the construction of a class and therefore you will be not able to swap/fake its dependencies when writing tests.\n\nIn the following example, the `UsersController` needs an instance of the `UserService` class.\n\n```ts\n@inject()\nclass UsersController {\n  constructor(service: UserService) {}\n}\n```\n\nIn the following test, we are making an HTTP request that will be handled by the `UsersController`. However, within the test, we have no control over the construction of the controller class.\n\n```ts\ntest('get all users', async ({ client }) =\u003e {\n  // I WANTED TO FAKE USER SERVICE FIRST?\n  const response = await client.get('users')\n})\n```\n\nTo make things simpler, you can tell the container to use a swapped implementation for a given class constructor as follows.\n\n```ts\ntest('get all users', async ({ client }) =\u003e {\n  class MyFakedService extends UserService {}\n\n  /**\n   * From now on, the container will return an instance\n   * of `MyFakedService`.\n   */\n  container.swap(UserService, () =\u003e new MyFakedService())\n\n  const response = await client.get('users')\n})\n```\n\n## Observing container\n\nYou can pass an instance of the [EventEmitter](https://nodejs.org/dist/latest-v18.x/docs/api/events.html#class-eventemitter) or [emittery](https://github.com/sindresorhus/emittery) to listen for events as container resolves dependencies.\n\n```ts\nimport { EventEmitter } from 'node:events'\nconst emitter = new EventEmitter()\n\nemitter.on('container:resolved', ({ value, binding }) =\u003e {\n  // value is the resolved value\n  // binding name can be a mix of string, class constructor, or a symbol.\n})\n\nconst container = new Container({ emitter })\n```\n\n## Container hooks\n\nYou can use container hooks when you want to modify a resolved value before it is returned from the `make` method.\n\n- The hook is called everytime a binding is resolved from the container.\n- It is called only once for the singleton bindings.\n- The hook gets called everytime you construct an instance of a class by passing the class constructor directly.\n\n\u003e **Note**: The hook callback can also be an async function\n\n```ts\ncontainer.resolving(Validator, (validator) =\u003e {\n  validate.rule('email', function () {})\n})\n```\n\n## Container providers\n\nContainer providers are static functions that can live on a class to resolve the dependencies for the class constructor or a given class method.\n\nOnce, you define the `containerProvider` on the class, the IoC container will rely on it for resolving dependencies and will not use the default provider.\n\n```ts\nimport { ContainerResolver } from '@adonisjs/fold'\nimport { ContainerProvider } from '@adonisjs/fold/types'\n\nclass UsersController {\n  static containerProvider: ContainerProvider = (\n    binding,\n    property,\n    resolver,\n    defaultProvider,\n    runtimeValues\n  ) =\u003e {\n    console.log(binding === UserService)\n    console.log(this === UserService)\n    return defaultProvider(binding, property, resolver, runtimeValues)\n  }\n}\n```\n\n### Why would I use custom providers?\n\nCustom providers can be handy when creating an instance of the class is not enough to construct it properly.\n\nLet's take an example of [AdonisJS route model binding](https://github.com/adonisjs/route-model-binding). With route model binding, you can query the database using models based on the value of a route parameter and inject the model instance inside the controller.\n\n```ts\nimport User from '#models/User'\nimport { bind } from '@adonisjs/route-model-binding'\n\nclass UsersController {\n  @bind()\n  public show(_, user: User) {}\n}\n```\n\nNow, if you use the `@inject` decorator to resolve the `User` model, then the container will only create an instance of User and give it back to you.\n\nHowever, in this case, we want more than just creating an instance of the model. We want to look up the database and create an instance with the row values.\n\nThis is where the `@bind` decorator comes into the picture. To perform database lookups, it registers a custom provider on the `UsersController` class.\n\n## Binding types\n\nIf you are using the container inside a TypeScript project, then you can define the types for all the bindings in advance at the time of creating the container instance.\n\nDefining types will ensure the `bind`, `singleton` and `bindValue` method accepts only the known bindings and assert their types as well.\n\n```ts\nclass Route {}\nclass Databse {}\n\ntype ContainerBindings = {\n  route: Route\n  db: Database\n}\n\nconst container = new Container\u003cContainerBindings\u003e()\n\n// Fully typed\ncontainer.bind('route', () =\u003e new Route())\ncontainer.bind('db', () =\u003e new Database())\n\n// Fully typed - db: Database\nconst db = await container.make('db')\n```\n\n## Common errors\n\n### Cannot inject \"xxxxx\" in \"[class: xxxxx]\". The value cannot be constructed\n\nThe error occurs, when you are trying to inject a value that cannot be constructed. A common source of issue is within TypeScript project, when using an `interface` or a `type` for dependency injection.\n\nIn the following example, the `User` is a TypeScript type and there is no way for the container to construct a runtime value from this type (types are removed after transpiling the TypeScript code).\n\nTherefore, the container will raise an exception saying `Cannot inject \"[Function: Object]\" in \"[class: UsersController]\". The value cannot be constructed`.\n\n```ts\ntype User = {\n  username: string\n  age: number\n  email: string\n}\n\n@inject()\nclass UsersController {\n  constructor(user: User)\n}\n```\n\n## Module expressions\n\nIn AdonisJS, we allow binding methods in form of string based module expression. For example:\n\n**Instead of importing and using a controller as follows**\n\n```ts\nimport UsersController from '#controllers/users'\n\nRoute.get('users', (ctx) =\u003e {\n  return new UsersController().index(ctx)\n})\n```\n\n**You can bind the controller method as follows**\n\n```ts\nRoute.get('users', '#controllers/users.index')\n```\n\n**Why do we do this?**\nThere are a couple of reasons for using module expressions.\n\n- Performance: Lazy loading controllers ensures that we keep the application boot process quick. Otherwise, we will pull all the controllers and their imports within a single routes file and that will surely impact the boot time of the application.\n- Visual clutter: Imagine importing all the controllers within a single routes file (or maybe 2-3 different route files) and then instantiating them manually. This surely brings some visual clutter in your codebase (agree visual clutter is subjective).\n\nSo given we use **module expressions** widely in the AdonisJS ecosystem. We have abstracted the logic of parsing string based expressions into dedicated helpers to re-use and ease.\n\n### Assumptions\n\nThere is a strong assumption that every module references using module expression will have a `export default` exporting a class.\n\n```ts\n// Valid module for module expression\nexport default class UsersController {}\n```\n\n### toCallable\n\nThe `toCallable` method returns a function that internally parses the module string expression and returns a function that you can invoke like any other JavaScript function.\n\n```ts\nimport { moduleExpression } from '@adonisjs/fold'\n\nconst fn = moduleExpression('#controllers/users.index', import.meta.url).toCallable()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait fn(resolver, [ctx])\n```\n\nYou can also pass the container instance at the time of creating the callable function.\n\n```ts\nconst container = new Container()\nconst fn = moduleExpression('#controllers/users.index', import.meta.url).toCallable(container)\n\n// Later call it\nawait fn([ctx])\n```\n\n### toHandleMethod\n\nThe `toHandleMethod` method returns an object with the `handle` method. To the main difference between `toCallable` and `toHandleMethod` is their return output\n\n- `toHandleMethod` returns `{ handle: fn }`\n- `toCallable` returns `fn`\n\n```ts\nimport { moduleExpression } from '@adonisjs/fold'\n\nconst handler = moduleExpression('#controllers/users.index', import.meta.url).toHandleMethod()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait handler.handle(resolver, [ctx])\n```\n\nYou can also pass the container instance at the time of creating the handle method.\n\n```ts\nconst container = new Container()\n\nconst handler = moduleExpression('#controllers/users.index', import.meta.url).toHandleMethod(\n  container\n)\n\n// Later call it\nawait handler.handle([ctx])\n```\n\n### Bechmarks\n\nFollowing are benchmarks to see the performance loss that happens when using module expressions.\n\n**Benchmarks were performed on Apple M1 iMac, 16GB**\n\n![](./benchmarks.png)\n\n- `handler`: Calling the handle method on the output of `toHandleMethod`.\n- `callable`: Calling the function returned by the `toCallable` method.\n- `native`: Using dynamic imports to lazily import the module. This variation does not use any helpers from this package.\n- `inline`: When no lazy loading was performed. The module was importing inline using the `import` keyword.\n\n## Module importer\n\nThe module importer is similar to module expression. However, instead of defining the import path as a string, you have to define a function that imports the module.\n\n```ts\nimport { moduleImporter } from '@adonisjs/fold'\n\nconst fn = moduleImporter(\n  () =\u003e import('#middleware/auth')\n  'handle'\n).toCallable()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait fn(resolver, [ctx])\n```\n\nCreate handle method object\n\n```ts\nimport { moduleImporter } from '@adonisjs/fold'\n\nconst handler = moduleImporter(\n  () =\u003e import('#middleware/auth')\n  'handle'\n).toHandleMethod()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait handler.handle(resolver, [ctx])\n```\n\n## Module caller\n\nThe module caller is similar to module importer. However, instead of lazy loading a class, you pass the class constructor to this method.\n\n```ts\nimport { moduleCaller } from '@adonisjs/fold'\n\nclass AuthMiddleware {\n  handle() {}\n}\n\nconst fn = moduleCaller(AuthMiddleware, 'handle').toCallable()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait fn(resolver, [ctx])\n```\n\nCreate handle method object\n\n```ts\nimport { moduleCaller } from '@adonisjs/fold'\n\nclass AuthMiddleware {\n  handle() {}\n}\n\nconst handler = moduleImporter(AuthMiddleware, 'handle').toHandleMethod()\n\n// Later call it\nconst container = new Container()\nconst resolver = container.createResolver()\nawait handler.handle(resolver, [ctx])\n```\n\n[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/fold/checks.yml?style=for-the-badge\n[gh-workflow-url]: https://github.com/adonisjs/fold/actions/workflows/checks.yml 'Github action'\n[npm-image]: https://img.shields.io/npm/v/@adonisjs/fold/latest.svg?style=for-the-badge\u0026logo=npm\n[npm-url]: https://www.npmjs.com/package/@adonisjs/fold/v/latest 'npm'\n[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge\u0026logo=typescript\n[license-url]: LICENSE.md\n[license-image]: https://img.shields.io/github/license/adonisjs/fold?style=for-the-badge\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Ffold","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadonisjs%2Ffold","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Ffold/lists"}