{"id":15984510,"url":"https://github.com/metcoder95/fastify-racing","last_synced_at":"2025-03-16T07:32:00.694Z","repository":{"id":39994380,"uuid":"481943823","full_name":"metcoder95/fastify-racing","owner":"metcoder95","description":"Cancel any running operation at the right time on your request handler","archived":false,"fork":false,"pushed_at":"2024-12-10T07:48:54.000Z","size":60,"stargazers_count":13,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-13T11:05:45.041Z","etag":null,"topics":["abort-signal","abortcontroller","fastify","fastify-library","fastify-plugin","http","http2","racing"],"latest_commit_sha":null,"homepage":"","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/metcoder95.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2022-04-15T11:57:55.000Z","updated_at":"2025-01-28T22:09:18.000Z","dependencies_parsed_at":"2024-01-01T23:40:17.556Z","dependency_job_id":"f20ec7ca-6ced-4738-b275-349b329e149a","html_url":"https://github.com/metcoder95/fastify-racing","commit_stats":{"total_commits":50,"total_committers":2,"mean_commits":25.0,"dds":0.48,"last_synced_commit":"0ecd5028e1696327befa3882f43e8e63b683f83e"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":"metcoder95/lib_template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Ffastify-racing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Ffastify-racing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Ffastify-racing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Ffastify-racing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metcoder95","download_url":"https://codeload.github.com/metcoder95/fastify-racing/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243806045,"owners_count":20350775,"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":["abort-signal","abortcontroller","fastify","fastify-library","fastify-plugin","http","http2","racing"],"created_at":"2024-10-08T02:09:14.907Z","updated_at":"2025-03-16T07:32:00.331Z","avatar_url":"https://github.com/metcoder95.png","language":"JavaScript","readme":"# fastify-racing\n\n[![CI](https://github.com/metcoder95/fastify-racing/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/metcoder95/fastify-racing/actions/workflows/ci.yml) [![CodeQL](https://github.com/metcoder95/fastify-racing/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/metcoder95/fastify-racing/actions/workflows/codeql-analysis.yml) [![version](https://badge.fury.io/js/fastify-racing.svg)](https://badge.fury.io/js/fastify-racing)\n\n---\n\n`fastify-racing` is a plugin which allows you handle possible client request abortions by exposing an `AbortSignal` instance that will be aborted just and only when the client has closed the request abruptly (e.g. by closing the browser tab).\n\n## How it works?\n\nOn every request and after a first invocation, the plugin well schedule event listeners to the [`close`](https://nodejs.org/api/net.html#event-close_1) event triggered by the [Socket](https://nodejs.org/api/net.html#new-netsocketoptions) instance attached to the  request object.\n\nAlong with that, the plugin will instanciate and cache an [`AbortController`](https://nodejs.org/api/globals.html#class-abortcontroller) instance for each request.\n\nWhen the `close` event is triggered, the plugin will check if the [`AbortSignal`](https://nodejs.org/api/globals.html#class-abortsignal) instance is already aborted, and if not will abort it using the `AbortController` instance.\n\nIs guaranteed that one and just one `AbortController` and `AbortSignal` will be made per request.\n\nIf the request was not aborted during its lifetime, the plugin will remove the `AbortController` and `AbortSignal` from the cache. This by scheduling a hook-handler on the hook `onResponse`.\n\nIf the request aborted, the same hook will be used for cleaning resources.\n\nA [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) is used under the hood for caching, ensuring that the `AbortController` and `AbortSignal` instances can be unlinked if not needed anymore, and for instance GC'ed.\n\n## Setup\n\nInstall by running `npm install fastify-racing`.\n\nThen register the plugin to your fastify instance:\n\n```js\nconst fastify = require('fastify')({\n  logger: true\n})\n\nfastify.register(require('fastify-racing'), {\n    handleError: true,\n})\n```\n\n### Options\n\n**On Setup**\n\n- `handleError`: Indicates to the pluging if an event listener to the Socket [`error`](https://nodejs.org/api/net.html#event-error_1) event should be attached or not. Default `true`.\n\n- `onRequestClosed`: Default callback to be used of none is passed during runtime It will receive as argument the event object similar to the `abort` event handler. Default `null`\n\n\n## How to use it?\n\nThere are two ways to use this plugin:\n\n### Promise\n\nIt will return a promise that will be resolved when the request is aborted. It will be resolved with the result of the [`abort`](https://nodejs.org/api/globals.html#event-abort) event object of the `AbortSignal` instance. This only if no `cb` has been passed as argument.\n\nIt supports an object as argument:\n\n- `opts.handleError`: [Optional] Indicates to the plugin to ignore or listen to the Socket [`error`](https://nodejs.org/api/net.html#event-error_1) event. Default to `pluginOption.handleError` passed when registering the pluging or `false`.\n\n**JavaScript**\n\n```js\napp.get('/', async (req, _reply) =\u003e {\n    const signal = req.race()\n    const result = await Promise.race([signal, asyncOp(signal)])\n\n    if (result.type === 'aborted') return ''\n    else return `${result}-world`\n})\n```\n\n**TypeScript**\n```ts\napp.post('/', (request: FastifyRequest, reply: FastifyReply) =\u003e {\n    const signal = req.race()\n    const result: AbortEvent | unknown = await Promise.race([signal, asyncOp(signal)])\n\n    if ((\u003cAbortEvent\u003eresult).type === 'aborted') return ''\n    else return `${result}-world`\n});\n```\n\n\n### Callback\n\nIf a callback is provided, no promise will be scheduled/returned during the lifetime of the request.\n\n- `cb`: Similar signature as `onRequestClosed`. Default `undefined` or to `onRequestClosed` if passed when registering the plugin.\n\n**JavaScript**\n\n```js\napp.get('/', (req, reply) =\u003e {\n    const signal = req.race((evt) =\u003e {\n        const result = result.type === 'aborted' ? '' : `${result}-world`\n\n        reply.send(result)\n    })\n})\n```\n\n**TypeScript**\n\n```ts\napp.post('/', (request: FastifyRequest, reply: FastifyReply) =\u003e {\n    const signal = req.race((evt: AbortEvent) =\u003e {\n        reply.send('')\n    })\n});\n```\n\n## Type Definitions\n\n```ts\ninterface AbortEvent {\n    type: 'abort' | string;\n    reason?: FastifyError | Error\n}\n\ninterface FastifyRacing {\n  handleError?: boolean;\n  onRequestClosed?: (evt: AbortEvent) =\u003e void;\n}\n\ninterface FastifyInstance {\n    race(cb: FastifyRacing['onRequestClosed']): void\n    race(opts: Omit\u003cFastifyRacing, 'onRequestClosed'\u003e): Promise\u003cAbortEvent\u003e\n    race(): Promise\u003cAbortEvent\u003e\n}\n```\n\n\n\u003e See [test](test/index.test.js) for more examples.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetcoder95%2Ffastify-racing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetcoder95%2Ffastify-racing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetcoder95%2Ffastify-racing/lists"}