{"id":16837659,"url":"https://github.com/jeffijoe/awilix-koa","last_synced_at":"2025-04-09T06:09:44.740Z","repository":{"id":10622431,"uuid":"66378539","full_name":"jeffijoe/awilix-koa","owner":"jeffijoe","description":"Awilix helpers/middleware for Koa 2","archived":false,"fork":false,"pushed_at":"2024-09-12T18:17:57.000Z","size":1527,"stargazers_count":129,"open_issues_count":1,"forks_count":26,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-14T12:18:11.593Z","etag":null,"topics":["awilix","awilix-helpers","dependency-injection","koa","middleware"],"latest_commit_sha":null,"homepage":null,"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/jeffijoe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2016-08-23T15:17:15.000Z","updated_at":"2024-09-28T12:06:56.000Z","dependencies_parsed_at":"2024-06-18T14:46:52.625Z","dependency_job_id":"dad2d9eb-9bcf-403f-b38f-532daf961129","html_url":"https://github.com/jeffijoe/awilix-koa","commit_stats":{"total_commits":69,"total_committers":4,"mean_commits":17.25,"dds":0.07246376811594202,"last_synced_commit":"beba17b842bd0668a28f005f1de28e95a80effcc"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffijoe%2Fawilix-koa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffijoe%2Fawilix-koa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffijoe%2Fawilix-koa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffijoe%2Fawilix-koa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeffijoe","download_url":"https://codeload.github.com/jeffijoe/awilix-koa/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987285,"owners_count":21028895,"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":["awilix","awilix-helpers","dependency-injection","koa","middleware"],"created_at":"2024-10-13T12:18:22.814Z","updated_at":"2025-04-09T06:09:44.724Z","avatar_url":"https://github.com/jeffijoe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# awilix-koa\n\n[![npm version](https://badge.fury.io/js/awilix-koa.svg)](https://badge.fury.io/js/awilix-koa)\n[![CI](https://github.com/jeffijoe/awilix-koa/actions/workflows/ci.yml/badge.svg)](https://github.com/jeffijoe/awilix-koa/actions/workflows/ci.yml)\n[![Coverage Status](https://coveralls.io/repos/github/jeffijoe/awilix-koa/badge.svg?branch=master)](https://coveralls.io/github/jeffijoe/awilix-koa?branch=master)\n![Typings Included](https://img.shields.io/badge/typings-included-brightgreen.svg)\n\nAwilix helpers, router and scope-instantiating middleware for **Koa**. 🐨\n\n# Table of Contents\n\n- [awilix-koa](#awilix-koa)\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n- [Awesome Usage](#awesome-usage)\n- [Why do I need it?](#why-do-i-need-it)\n  - [Manual](#manual)\n  - [Using `awilix-koa`](#using-awilix-koa)\n- [API](#api)\n- [Contributing](#contributing)\n  - [`npm run` scripts](#npm-run-scripts)\n- [Author](#author)\n\n# Installation\n\n```\nnpm install --save awilix-koa\n```\n\n_Requires Node v6 or above_\n\n# Basic Usage\n\nAdd the middleware to your Koa app.\n\n```js\nconst { asClass, asValue, createContainer } = require('awilix')\nconst { scopePerRequest } = require('awilix-koa')\n\nconst container = createContainer()\ncontainer.register({\n  // Scoped lifetime = new instance per request\n  // Imagine the TodosService needs a `user`.\n  // class TodosService { constructor({ user }) { } }\n  todosService: asClass(TodosService).scoped()\n})\n\n// Add the middleware, passing it your Awilix container.\n// This will attach a scoped container on the context.\napp.use(scopePerRequest(container))\n\n// Now you can add request-specific data to the scope.\napp.use((ctx, next) =\u003e {\n  ctx.state.container.register({\n    user: asValue(ctx.state.user) // from some authentication middleware..\n  })\n  return next()\n})\n```\n\nThen in your route handlers...\n\n```js\nconst { makeInvoker } = require('awilix-koa')\n\nfunction makeAPI({ todosService }) {\n  return {\n    find: ctx =\u003e {\n      return todosService.find().then(result =\u003e {\n        ctx.body = result\n      })\n    }\n  }\n}\n\nconst api = makeInvoker(makeAPI)\n\n// Creates middleware that will invoke `makeAPI`\n// for each request, giving you a scoped instance.\nrouter.get('/todos', api('find'))\n```\n\n# Awesome Usage\n\n**As of `awilix-koa@1.0.0`**, we ship with `koa-router` bindings for [`awilix-router-core`][awilix-router-core]!\nThis is cool because now your routing setup can be streamlined with first-class Awilix support!\n\nThe Awilix-based router comes in 2 flavors: **a builder** and **ESNext decorators**.\n\n**`routes/todos-api.js`** - demos the builder pattern\n\n```js\nimport bodyParser from 'koa-bodyparser'\nimport { authenticate } from './your-auth-middleware'\nimport { createController } from 'awilix-koa' // or `awilix-router-core`\n\nconst API = ({ todoService }) =\u003e ({\n  getTodo: async ctx =\u003e (ctx.body = await todoService.get(ctx.params.id)),\n  createTodo: async ctx =\u003e\n    (ctx.body = await todoService.create(ctx.request.body))\n})\n\nexport default createController(API)\n  .prefix('/todos') // Prefix all endpoints with `/todo`\n  .before([authenticate()]) // run authentication for all endpoints\n  .get('/:id', 'getTodo') // Maps `GET /todos/:id` to the `getTodo` function on the returned object from `API`\n  .post('', 'createTodo', {\n    // Maps `POST /todos` to the `createTodo` function on the returned object from `API`\n    before: [bodyParser()] // Runs the bodyParser just for this endpoint\n  })\n```\n\n**`routes/users-api.js`** - demos the decorator pattern\n\n```js\nimport bodyParser from 'koa-bodyparser'\nimport { authenticate } from './your-auth-middleware'\nimport { route, GET, POST, before } from 'awilix-koa' // or `awilix-router-core`\n\n@route('/users')\nexport default class UserAPI {\n  constructor({ userService }) {\n    this.userService = userService\n  }\n\n  @route('/:id')\n  @GET()\n  @before([authenticate()])\n  async getUser(ctx) {\n    ctx.body = await this.userService.get(ctx.params.id)\n  }\n\n  @POST()\n  @before([bodyParser()])\n  async createUser(ctx) {\n    ctx.body = await this.userService.create(ctx.request.body)\n  }\n}\n```\n\n**`server.js`**\n\n```js\nimport Koa from 'koa'\nimport { asClass, createContainer } from 'awilix'\nimport { loadControllers, scopePerRequest } from 'awilix-koa'\n\nconst app = new Koa()\nconst container = createContainer().register({\n  userService: asClass(/*...*/),\n  todoService: asClass(/*...*/)\n})\napp.use(scopePerRequest(container))\n// Loads all controllers in the `routes` folder\n// relative to the current working directory.\n// This is a glob pattern.\napp.use(loadControllers('routes/*.js', { cwd: __dirname }))\n\napp.listen(3000)\n```\n\nPlease see the [`awilix-router-core`][awilix-router-core] docs for information about the full API.\n\n# Why do I need it?\n\nYou can certainly use Awilix with Koa without this library, but follow along and you might see why it's useful.\n\nImagine this simple imaginary Todos app, written in ES6:\n\n```js\n// A totally framework-independent piece of application code.\n// Nothing here is remotely associated with HTTP, Koa or anything.\nclass TodosService {\n  constructor({ currentUser, db }) {\n    // We depend on the current user!\n    this.currentUser = currentUser\n    this.db = db\n  }\n\n  getTodos() {\n    // use your imagination ;)\n    return this.db('todos').where('user', this.currentUser.id)\n  }\n}\n\n// Here's a Koa API that calls the service\nclass TodoAPI {\n  constructor({ todosService }) {\n    this.todosService = todosService\n  }\n  getTodos(ctx) {\n    return this.todosService.getTodos().then(todos =\u003e ctx.ok(todos))\n  }\n}\n```\n\nSo the problem with the above is that the `TodosService` needs a `currentUser` for it to function. Let's first try solving this manually, and then with `awilix-koa`.\n\n## Manual\n\nThis is how you would have to do it without Awilix at all.\n\n```js\nimport db from './db'\n\nrouter.get('/todos', ctx =\u003e {\n  // We need a new instance for each request,\n  // else the currentUser trick wont work.\n  const api = new TodoAPI({\n    todosService: new TodosService({\n      db,\n      // current user is request specific.\n      currentUser: ctx.state.user\n    })\n  })\n\n  // invoke the method.\n  return api.getTodos(ctx)\n})\n```\n\nLet's do this with Awilix instead. We'll need a bit of setup code.\n\n```js\nimport { asValue, createContainer, Lifetime } from 'awilix'\n\nconst container = createContainer()\n\n// The `TodosService` lives in services/TodosService\ncontainer.loadModules(['services/*.js'], {\n  // we want `TodosService` to be registered as `todosService`.\n  formatName: 'camelCase',\n  resolverOptions: {\n    // We want instances to be scoped to the Koa request.\n    // We need to set that up.\n    lifetime: Lifetime.SCOPED\n  }\n})\n\n// imagination is a wonderful thing.\napp.use(someAuthenticationMethod())\n\n// We need a middleware to create a scope per request.\n// Hint: that's the scopePerRequest middleware in `awilix-koa` ;)\napp.use((ctx, next) =\u003e {\n  // We want a new scope for each request!\n  ctx.state.container = container.createScope()\n  // The `TodosService` needs `currentUser`\n  ctx.state.container.register({\n    currentUser: asValue(ctx.state.user) // from auth middleware.. IMAGINATION!! :D\n  })\n  return next()\n})\n```\n\nOkay! Let's try setting up that API again!\n\n```js\nexport default function(router) {\n  router.get('/todos', ctx =\u003e {\n    // We have our scope available!\n    const api = new TodoAPI(ctx.state.container.cradle) // Awilix magic!\n    return api.getTodos(ctx)\n  })\n}\n```\n\nA lot cleaner, but we can make this even shorter!\n\n```js\nexport default function(router) {\n  // Just invoke `api` with the method name and\n  // you've got yourself a middleware that instantiates\n  // the API and calls the method.\n  const api = methodName =\u003e {\n    // create our handler\n    return function(ctx) {\n      const controller = new TodoAPI(ctx.state.container.cradle)\n      return controller[method](ctx)\n    }\n  }\n\n  // adding more routes is way easier!\n  router.get('/todos', api('getTodos'))\n}\n```\n\n## Using `awilix-koa`\n\nIn our route handler, do the following:\n\n```js\nimport { makeInvoker } from 'awilix-koa'\n\nexport default function(router) {\n  const api = makeInvoker(TodoAPI)\n  router.get('/todos', api('getTodos'))\n}\n```\n\nAnd in your Koa application setup:\n\n```js\nimport { asValue, createContainer, Lifetime } from 'awilix'\nimport { scopePerRequest } from 'awilix-koa'\n\nconst container = createContainer()\n\n// The `TodosService` lives in services/TodosService\ncontainer.loadModules(\n  [\n    ['services/*.js', Lifetime.SCOPED] // shortcut to make all services scoped\n  ],\n  {\n    // we want `TodosService` to be registered as `todosService`.\n    formatName: 'camelCase'\n  }\n)\n\n// imagination is a wonderful thing.\napp.use(someAuthenticationMethod())\n\n// Woah!\napp.use(scopePerRequest(container))\napp.use((ctx, next) =\u003e {\n  // We still want to register the user!\n  // ctx.state.container is a scope!\n  ctx.state.container.register({\n    currentUser: asValue(ctx.state.user) // from auth middleware.. IMAGINATION!! :D\n  })\n})\n```\n\nNow **that** is way simpler!\n\n```js\nimport { makeInvoker } from 'awilix-koa'\n\nfunction makeTodoAPI({ todosService }) {\n  return {\n    getTodos: ctx =\u003e {\n      return todosService.getTodos().then(todos =\u003e ctx.ok(todos))\n    }\n  }\n}\n\nexport default function(router) {\n  const api = makeInvoker(makeTodoAPI)\n  router.get('/api/todos', api('getTodos'))\n}\n```\n\nThat concludes the tutorial! Hope you find it useful, I know I have.\n\n# API\n\nThe package exports everything from `awilix-router-core` as well as the following **Koa middleware factories**:\n\n- `scopePerRequest(container)`: creates a scope per request.\n- `attachContainer(container)`: permits use of awilix-koa without creating a scope per request.\n- `controller(decoratedClassOrController)`: registers routes and delegates to Koa Router.\n- `importControllers(router, pattern, opts)`: imports files matching a glob pattern, registers their exports as controllers, applying them to the supplied koa-router\n- `loadControllers(pattern, opts, router)`: loads files matching a glob pattern and registers their exports as controllers and returns a middleware for use with Koa\n- `makeInvoker(functionOrClass, opts)(methodName)`: using `isClass`, calls either `makeFunctionInvoker` or `makeClassInvoker`.\n- `makeClassInvoker(Class, opts)(methodName)`: resolves \u0026 calls `methodName` on the resolved instance, passing it `ctx` and `next`.\n- `makeFunctionInvoker(function, opts)(methodName)`: resolves \u0026 calls `methodName` on the resolved instance, passing it `ctx` and `next`.\n- `makeResolverInvoker(resolver, opts)`: used by the other invokers, exported for convenience.\n- `inject(middlewareFactory)`: resolves the middleware per request.\n  ```js\n  app.use(\n    inject(({ userService }) =\u003e (ctx, next) =\u003e {\n      /**/\n    })\n  )\n  ```\n\n# Contributing\n\n## `npm run` scripts\n\n- `npm run test`: Runs tests once\n- `npm run lint`: Lints + formats the code once\n- `npm run cover`: Runs code coverage using `istanbul`\n\n# Author\n\nJeff Hansen - [@Jeffijoe](https://twitter.com/Jeffijoe)\n\n[awilix-router-core]: https://github.com/jeffijoe/awilix-router-core\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffijoe%2Fawilix-koa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeffijoe%2Fawilix-koa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffijoe%2Fawilix-koa/lists"}