{"id":29228972,"url":"https://github.com/izeau/cerise","last_synced_at":"2025-12-30T22:18:26.718Z","repository":{"id":57196129,"uuid":"185006565","full_name":"izeau/cerise","owner":"izeau","description":"Intuitive and lightweight Dependency Injection library for Node.js","archived":false,"fork":false,"pushed_at":"2019-05-05T10:42:59.000Z","size":76,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-05-31T03:44:13.060Z","etag":null,"topics":["container","controller","dependency","di","express","factory","injection","koa","middleware","nodejs","npm","proxy","service","singleton"],"latest_commit_sha":null,"homepage":null,"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/izeau.png","metadata":{"files":{"readme":"readme.md","changelog":null,"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":"2019-05-05T09:01:58.000Z","updated_at":"2022-09-08T08:29:30.000Z","dependencies_parsed_at":"2022-09-16T12:21:07.588Z","dependency_job_id":null,"html_url":"https://github.com/izeau/cerise","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/izeau/cerise","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izeau%2Fcerise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izeau%2Fcerise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izeau%2Fcerise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izeau%2Fcerise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izeau","download_url":"https://codeload.github.com/izeau/cerise/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izeau%2Fcerise/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263229087,"owners_count":23434003,"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":["container","controller","dependency","di","express","factory","injection","koa","middleware","nodejs","npm","proxy","service","singleton"],"created_at":"2025-07-03T11:05:28.980Z","updated_at":"2025-12-30T22:18:26.682Z","avatar_url":"https://github.com/izeau.png","language":"JavaScript","readme":"# Cerise\n\n![](https://img.shields.io/npm/l/cerise.svg?logo=npm)\n![](https://img.shields.io/npm/v/cerise.svg?logo=npm)\n![](https://img.shields.io/badge/dependencies-none-green.svg?logo=npm)\n![](https://img.shields.io/travis/izeau/cerise.svg?logo=travis)\n![](https://img.shields.io/coveralls/github/izeau/cerise.svg?logo=travis)\n\nIntuitive _Dependency Injection (DI)_ library for Node.js, written in JavaScript and weighing less than 10 KB. Ironically, Cerise does not depend on any package.\n\n[API documentation](/api.md) -- [Examples](/examples) -- [FAQ](#faq)\n\n# Installation\n\nInstall with `npm` or `yarn`\n\n```shell\n$ npm install cerise\n$ yarn add cerise\n```\n\nBoth _CommonJS_ and _ES modules_ builds are included; the latter will be automatically selected if your build system supports it.\n\n# Usage\n\nUsing Cerise is _dead simple_. There are two concepts you'll need to understand first: _containers_ and _factories_.\n\n## createContainer\n\nA _container_ (also known as an _injector_) is a master object that knows how to create services, thanks to _factories_.\n\n```js\nconst { createContainer, constant, factory, service } = require('cerise');\n\n// Create a container and immediately register a factory\n// for the `name` service.\nconst container = createContainer({\n  package_name: constant('cerise'),\n});\n\n// You can also register a service for an existing container.\ncontainer.register('package_name', constant('cerise'));\n\n// You can retrieve services using either container as a\n// function, or its `proxy` property.\nassert('cerise' === container('package_name'));\nassert('cerise' === container.proxy.package_name);\n```\n\nThere are multiple ways to declare a service: using the `constant`, `factory` and `service` helpers.\n\n## constant\n\nWhen using `constant` you cannot depend on an other service. You can register any value: a number, string, function, etc.\n\n```js\ncontainer.register('base_url', constant('https://npmjs.com'));\ncontainer.register('concat', constant((...args) =\u003e args.join('')));\n\nassert('string' === typeof container('base_url'));\nassert('function' === typeof container('concat'));\n```\n\n## factory\n\nIf you need to depend on an other service, use a factory. `factory` takes a function that will be passed `container.proxy` (which can be destructured to access other services) and returns a service.\n\n```js\ncontainer.register(\n  'package_url',\n  factory(proxy =\u003e {\n    return proxy.concat(proxy.base_url, '/', proxy.package_name);\n  }),\n);\n\n// Using destructuring\ncontainer.register(\n  'package_url',\n  factory(({ concat, base_url: baseUrl, package_name: packageName }) =\u003e {\n    return concat(baseUrl, '/', packageName);\n  }),\n);\n\n// Alternatively, call the proxy as a function\ncontainer.register(\n  'package_url',\n  factory(inject =\u003e {\n    return inject('concat')(inject('base_url'), '/', inject('package_name'));\n  }),\n);\n\nassert('https://npmjs.com/cerise' === container('package_url'));\n```\n\nYou'll notice that `constant(x)` is equivalent to `factory(() =\u003e x)`: it's just sugar.\n\n## service\n\nLastly, `service` is passed a class and will return an instance on retrieval. Use it if you're more familiar with OOP.\n\n```js\nclass PackageUrl {\n  constructor({ concat, base_url: baseUrl }) {\n    this._concat = concat;\n    this._baseUrl = baseUrl;\n  }\n\n  get(packageName) {\n    return this._concat(this._baseUrl, '/', packageName);\n  }\n}\n\ncontainer.register('package_url', service(PackageUrl));\n\nassert('https://npmjs.com/cerise' === container('package_url').get('cerise'));\n```\n\nOnce again, it's just sugar: `service(T)` is equivalent to `factory(proxy =\u003e new T(proxy))`.\n\n# Scopes\n\nOftentimes you'll want to create a _scope_ from a container. _Scopes_ inherit their parent and their registered service, but can also have their own service. For instance, if you're using Express, you might want to have a master container to store your database connexion, and another container for request-specific data.\n\n```js\nconst express = require('express');\nconst { Database } = require('sqlite3');\nconst { createContainer, constant } = require('cerise');\n\nconst app = express();\nconst container = createContainer({\n  db: constant(new Database(':memory:')),\n});\n\n// For each request, create a scope and fetch session data.\napp.use((req, res, next) =\u003e {\n  const id = req.get('x-session-id');\n  const db = container('db');\n\n  req.scope = container.scope();\n\n  db.get('select * from sessions where id = ?', [id], (err, session) =\u003e {\n    // Only alter the request scope, not the parent container\n    req.scope.register('session', constant(session));\n    next();\n  });\n});\n\n// Session data is available on child scope.\napp.get('/session', (req, res) =\u003e {\n  res.json(req.scope('session'));\n});\n\n// Parent container services are also available on child scope.\napp.get('/time', (req, res) =\u003e {\n  const db = req.scope('db');\n\n  db.get('select current_timestamp as time', (err, { time }) =\u003e {\n    res.json({ time });\n  });\n});\n```\n\n# Lifetimes\n\n## default lifetime\n\nBy default (except for `constant`) the factory will be called each time you wish to retrieve a value from a factory.\n\n```js\n// Each resolution will result in a new Thing instance being created.\ncontainer.register('thing', service(class Thing {}));\n\nconst foo = container('thing');\nconst bar = container('thing');\n\nassert(foo !== bar);\n```\n\n## singletons\n\nYou may however wish to specify a lifetime to your factory in order to cache its result.\n\n```js\n// Now the first instance will be cached and returned each time.\ncontainer.register('thing', service(class Thing {}).singleton());\n\nconst foo = container('thing');\nconst bar = container('thing');\n\nassert(foo === bar);\n```\n\n## scoped\n\nSingletons only make sense on the root container; if you wish to cache a service for scopes you will want to use the `scoped` lifetime qualifier:\n\n```js\nconst winston = require('winston');\n\ncontainer.register(\n  'logger',\n  constant(\n    winston.createLogger({\n      transports: winston.transports.Console(),\n    }),\n  ),\n);\n\n// Create a scope on every request\napp.use((req, res, next) =\u003e {\n  req.scope = container.scope();\n  next();\n});\n\n// Register a *scoped* logger (with request id metadata)\napp.use((req, res, next) =\u003e {\n  req.scope.register(\n    'reqlog',\n    factory(({ logger }) =\u003e\n      logger.child({ requestId: req.get('x-request-id') }),\n    ).scoped(),\n  );\n  next();\n});\n\n// Logging middleware\napp.use((req, res, next) =\u003e {\n  const start = Date.now();\n  const logger = req.scope('reqlog');\n\n  req.on('finish', () =\u003e {\n    const elapsed = Date.now() - start;\n\n    logger.info('[%s] %s %s', elapsed, req.method, req.path);\n  });\n\n  next();\n});\n```\n\n# Saving and restoring state\n\nRoot containers' state can be saved and restored which can be useful for testing. For instance, in Mocha's `beforeEach` and `afterEach` hooks:\n\n```js\ndescribe('My API', () =\u003e {\n  beforeEach(() =\u003e container.save());\n  afterEach(() =\u003e container.restore());\n\n  it('...', () =\u003e {\n    // package_url service will be 'nope' but only for this particular test\n    container.register('package_url', constant('nope'));\n  });\n});\n```\n\n# Utils\n\n**Middlewares:** Cerise provides middlewares for Express and Koa. \\\nSee the [API documentation](/api.md#middleware).\n\n**Controllers:** since calling `req.scope` gets old really fast, Cerise also provides a `controller` helper -- with an async error handler for convenience. Pass it a callback, and it will get called with `req.scope.proxy`, `req`, `res` and `next`. \\\nSee the [API documentation](/api.md#controller).\n\n# FAQ\n\n## How to overwrite a parent service in a scope?\n\nYou can register a service with the same name:\n\n```js\nconst parent = createContainer();\nparent.register('scopeName', constant('parent'));\n\nconst child = parent.scope();\nchild.register('scopeName', constant('child'));\n\nassert('parent' === parent('scopeName'));\nassert('child' === child('scopeName'));\n```\n\n## Can a child scope service depend on a parent scope service of the same name?\n\nYes, but you cannot depend directly on the parent service.\n\n```js\nparent.register('breadcrumb', constant('/'));\n\n// This will break: `breadcrumb` on the child cannot depend on `breadcrumb`.\nchild.register(\n  'breadcrumb',\n  factory(({ breadcrumb }) =\u003e breadcrumb + 'child/'),\n);\n\n// Workaround #1: access parent scope directly\nchild.register('breadcrumb', factory(() =\u003e parent('breadcrumb') + 'child/'));\n\n// Workaround #2 (preferred): register the parent as a child service\nchild.register('$parent', constant(parent.scope));\nchild.register(\n  'breadcrumb',\n  factory(({ $parent: { breadcrumb } }) =\u003e breadcrumb + 'child/'),\n);\n```\n\n# Examples\n\nHead over to the [examples](/examples) directory for in-depth examples.\n\n# Contributing\n\nConstructive feedback is always welcome! Feel free to create issues if you have any question, suggestion or bug reports. A pull request is also always appreciated.\n\nClone this repository, run `npm install` or `yarn` to install the development dependencies, launch `npm test -- -w` or `yarn test -w` and start hacking!\n\nBefore you submit your pull request, please make sur you've run Prettier (`npm run lint` or `yarn lint`) and that your test coverage is at 100% (`npm run coverage` or `yarn coverage`).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizeau%2Fcerise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizeau%2Fcerise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizeau%2Fcerise/lists"}