{"id":50584907,"url":"https://github.com/matthyk/fastify-override","last_synced_at":"2026-06-05T05:27:01.475Z","repository":{"id":224081812,"uuid":"762361718","full_name":"matthyk/fastify-override","owner":"matthyk","description":"Fastify plugin to override decorators, plugins and hooks","archived":false,"fork":false,"pushed_at":"2025-08-18T10:24:04.000Z","size":40,"stargazers_count":8,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-19T04:55:20.908Z","etag":null,"topics":["context","encapsulation","fastify","fatsify-plugin","intercept","mock","override","plugin","spy","testing"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/fastify-override","language":"JavaScript","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/matthyk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2024-02-23T16:10:34.000Z","updated_at":"2025-07-07T09:31:45.000Z","dependencies_parsed_at":"2025-01-15T18:06:35.252Z","dependency_job_id":"57edb493-232b-4979-b122-e59f60c9fbf7","html_url":"https://github.com/matthyk/fastify-override","commit_stats":null,"previous_names":["matthyk/fastify-override"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/matthyk/fastify-override","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthyk%2Ffastify-override","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthyk%2Ffastify-override/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthyk%2Ffastify-override/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthyk%2Ffastify-override/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matthyk","download_url":"https://codeload.github.com/matthyk/fastify-override/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthyk%2Ffastify-override/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33932039,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["context","encapsulation","fastify","fatsify-plugin","intercept","mock","override","plugin","spy","testing"],"created_at":"2026-06-05T05:27:00.942Z","updated_at":"2026-06-05T05:27:01.469Z","avatar_url":"https://github.com/matthyk.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fastify-override\n\n![CI](https://github.com/matthyk/fastify-override/actions/workflows/ci.yml/badge.svg)\n![CodeQL](https://github.com/matthyk/fastify-override/actions/workflows/codeql.yml/badge.svg)\n\nOverride any plugin, decorator or hook in your Fastify application. This is useful for mocking specific \nfunctionalities when testing your application. For the motivation of this plugin see [here](#why-is-this-plugin-useful).\n\n\u003e Note: You should use this plugin only for testing purposes.\n\n## Install\n\n```sh\nnpm install fastify-override --save-dev\n```\n\n### Compatibility\n\n| Plugin Version | Fastify Version | Node Versions |\n|----------------|:---------------:|---------------|\n| 1.x            |       4.x       | 18, 20, 21    |\n| 2.x            |       5.x       | 20, 22        |\n\n## Usage\n\nThis plugin allows you to override specific plugins, decorators and hooks within the plugin hierarchy. This can happen \nfrom any level within the hierarchy, and you can also register this plugin multiple times. But as soon as the plugin is \nto be used, the register call must be awaited so that all subsequently added plugins, decorators and hooks are considered.\n\n\u003e Note: Only plugins that are not registered as promise are supported. `app.register(import('...'))` would result in \n\u003e incorrect behaviour. If this occurs, the plugin will log a message accordingly.\n\n```js\n\napp.decorate('foo', 'bar')\n\nawait app.register(fastifyOverride, {\n  override: {\n    decorators: {\n      decorate: {\n        foo: 'overridden',\n        num: 42\n      }\n    }\n  }\n})\n\napp.decorate('num', 1)\n\napp.foo // === 'bar'\napp.num // === 42\n```\n\nAs you can see above, only the decorators (and of course plugins and hooks) that were added after the plugin was \nregistered are actually taken into account. There are some differences in the way the plugins, decorators and hooks are \noverridden. We will therefore briefly look at these 3 separately.\n\n### Override Plugins\n\nOnly plugins that have been assigned a name with `fastify-plugin` can be overridden. This name can then be used to \nspecify which plugin should be used to override it.\n\n```js\nconst plg = fp(async app =\u003e {}, {\n  name: 'myPlugin' // \u003c-- Therefore, this must match with...\n})\n\nawait app.register(fastifyOverride, {\n  override: {\n    plugins: {\n      myPlugin: fp(async () =\u003e {}) // ...this name\n    }\n  }\n})\n```\n\nKeep in mind that the encapsulation behavior of the plugin can also be changed by using or not using `fastify-plugin`.\n\n### Override Decorators\n\nDecorators are identified by their name and type. Therefore, the plugin with the following options\n\n```js\n{\n  override: {\n    decorators: {\n      decorateReply: {\n        num: 1\n      }\n    }\n  }\n}\n```\nwould override `app.decorateReply('num', 2)` but not `app.decorateRequest('num', 3)`.\n\n### Override Hooks\n\nFor each [hook type](https://fastify.dev/docs/latest/Reference/Hooks/), you can provide one function. This function then \noverrides all hooks of this type. This can of course lead to different hook functions being overridden with the same function.\nPlease note that only hooks that have been added via the `addHook` API are being checked.\n\n```js\nawait app.register(fastifyOverride, {\n  override: {\n    hooks: {\n      onRequest: async () =\u003e {},\n      onSend: async () =\u003e {}\n    }\n  }\n})\n```\n\n## Options\n\nThe following options are available.\n\n```js\nimport Fastify from 'fastify'\n\nconst app = Fastify()\n\nawait app.register(import('fastify-override'), {\n  override: {\n    plugins: {},\n    decorators: {\n      decorate: {},\n      decorateRequest: {},\n      decorateReply: {}\n    },\n    hooks: {}\n  }\n})\n```\n\n### override\n\nUse the `override` object to specify which decorators, which plugins and which hooks are to be overwritten.\n\n```js\nimport Fastify from 'fastify'\n\nconst app = Fastify()\n\nawait app.register(import('fastify-override'), {\n  override: {\n    plugins: {\n      '@fastifyPostgres': async app =\u003e {}\n    },\n    decorators: {\n      decorate: {\n        db: {\n          query: async () =\u003e {}\n        } \n      }\n    },\n    hooks: {\n      onRequest: async (req, reply) =\u003e {}\n    }\n  }\n})\n```\n\n\n## Why is this plugin useful?\n\nLet's assume our Fastify application has the following structure:\n\n```\n│   \n├── routes\n│   ├── users/\n│   ├── articles/\n│   ├── ...\n│   ...\n│\n├── plugins\n│   ├── postgres\n│   ├── auth\n│   ├── ...\n│   ...\n│\n├── test\n│   ├── users-routes.test.js\n│   ├── ...\n│   ...\n│   \n└── server.js\n```\n\nIn most cases, our `server.js` file will look like this:\n\n```js\nimport fastifyAutoload from '@fastify/autoload'\nimport { fileURLToPath } from 'url'\nimport { dirname, join } from 'path'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport default async function (app, opts) {\n  \n  app.register(fastifyAutoload, {\n    dir: join(__dirname, 'plugins'),\n    encapsulate: false\n  })\n\n  app.register(fastifyAutoload, {\n    dir: join(__dirname, 'routes'),\n    encapsulate: true\n  })\n  \n}\n```\n\nWe would now like to test the routes within the `routes/users` folder. Let's do this in the \n`users-routes.test.js` file with the `tap` testing library.\n\n```js\nimport t from 'tap'\nimport Fastify from 'fastify'\nimport application from '../server.js'\n\nt.test('test users routes', async t =\u003e {\n  const app = Fastify()\n  \n  app.register(application)\n  \n  // app.pg is undefined\n  // so we cannot mock like this\n  // app.pg.query = () =\u003e 'mocked value'\n})\n```\n\nBecause a new context is created by the register call, we have no access to all functionalities that are added by the \nplugins in the plugin folder. For example, we couldn't mock the Postgres plugin now simply because we don't have access \nto the decorated `pg` value that the plugin actually decorates the Fastify instance with. You could of course argue that \nby using `fastify-plugin` we could prevent the creation of a new context. That would work in this example, but what if \nwe want to mock a plugin, decorator or hook that is deeper in the plugin hierarchy? \nThis is exactly where `fastify-override` comes to the rescue. `fastify-override` makes it possible to override plugins, \ndecorators and hooks in the entire plugin hierarchy. \n\nLet's look at a code example:\n```js\nimport t from 'tap'\nimport Fastify from 'fastify'\nimport application from '../server.js'\n\nt.test('test users routes', async t =\u003e {\n  const app = Fastify()\n\n  await app.register(fastifyOverride, {\n    override: {\n      plugins: {\n        // We can mock a whole plugin...\n        '@fastify/postgres': fp(async app =\u003e {\n          app.decorate('pg', {\n            query: () =\u003e 'mocked value',\n          })\n        })\n      },\n      decorators: {\n        decorate: {\n          // ...or just specific decorators \n          pg: {\n            query: () =\u003e 'mocked value'\n          }\n        }\n      }\n    }\n  })\n  \n  app.register(application)\n  \n  await app.ready()\n  \n  app.pg.query() // returns 'mocked value'\n})\n```\n\nAs we can see, this plugin allows us to overwrite entire plugins or just specific decorators. Thereby it does not matter \nhow deep in the plugin hierarchy the plugin and/or the decorators are registered.\n\n## License\n\nLicensed under [MIT](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthyk%2Ffastify-override","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthyk%2Ffastify-override","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthyk%2Ffastify-override/lists"}