Skip to content

Commit a093f8a

Browse files
authored
chore(docs): add documentation (#2)
1 parent 39f342e commit a093f8a

File tree

2 files changed

+136
-3
lines changed

2 files changed

+136
-3
lines changed

README.md

+135-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,135 @@
1-
# lib_template
2-
Template repository for bootstrap new libraries faster
1+
# fastify-racing
2+
3+
[![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)
4+
5+
---
6+
7+
`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).
8+
9+
## How it works?
10+
11+
On 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.
12+
13+
Along with that, the plugin will instanciate and cache an [`AbortController`](https://nodejs.org/api/globals.html#class-abortcontroller) instance for each request.
14+
15+
When 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.
16+
17+
Is guaranteed that one and just one `AbortController` and `AbortSignal` will be made per request.
18+
19+
If the request was not aborted during its lifetime, the plugin will remove the `AbortController` and `AbortSignal` from the cache. This by scheduling a callback on the hook `onResponse`.
20+
21+
If the request aborted, the same hook will be used for cleaning resources.
22+
23+
A [`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 unliked if not needed anymore, and for instance GC'ed.
24+
25+
## Setup
26+
27+
Install by running `npm install fastify-racing`.
28+
29+
Then register the plugin to your fastify instance:
30+
31+
```js
32+
const fastify = require('fastify')({
33+
logger: true
34+
})
35+
36+
fastify.register(require('fastify-racing'), {
37+
handleError: true,
38+
})
39+
```
40+
41+
### Options
42+
43+
**On Setup**
44+
45+
- `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`.
46+
47+
- `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`
48+
49+
50+
## How to use it?
51+
52+
There are two ways to use this plugin:
53+
54+
### Promise
55+
56+
It 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.
57+
58+
It supports an object as argument:
59+
60+
- `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`.
61+
62+
**JavaScript**
63+
64+
```js
65+
app.get('/', async (req, _reply) => {
66+
const signal = req.race()
67+
const result = await Promise.race([signal, asyncOp(signal)])
68+
69+
if (result.type === 'aborted') return ''
70+
else return `${result}-world`
71+
})
72+
```
73+
74+
**TypeScript**
75+
```ts
76+
app.post('/', (request: FastifyRequest, reply: FastifyReply) => {
77+
const signal = req.race()
78+
const result: AbortEvent | unknown = await Promise.race([signal, asyncOp(signal)])
79+
80+
if ((<AbortEvent>result).type === 'aborted') return ''
81+
else return `${result}-world`
82+
});
83+
```
84+
85+
86+
### Callback
87+
88+
If a callback is provided, no promise will be scheduled/returned during the lifetime of the request.
89+
90+
- `cb`: Similar signature as `onRequestClosed`. Default `undefined` or to `onRequestClosed` if passed when registering the plugin.
91+
92+
**JavaScript**
93+
94+
```js
95+
app.get('/', (req, reply) => {
96+
const signal = req.race((evt) => {
97+
const result = result.type === 'aborted' ? '' : `${result}-world`
98+
99+
reply.send(result)
100+
})
101+
})
102+
```
103+
104+
**TypeScript**
105+
106+
```ts
107+
app.post('/', (request: FastifyRequest, reply: FastifyReply) => {
108+
const signal = req.race((evt: AbortEvent) => {
109+
reply.send('')
110+
})
111+
});
112+
```
113+
114+
## Type Definitions
115+
116+
```ts
117+
interface AbortEvent {
118+
type: 'abort' | string;
119+
reason?: FastifyError | Error
120+
}
121+
122+
interface FastifyRacing {
123+
handleError?: boolean;
124+
onRequestClosed?: (evt: AbortEvent) => void;
125+
}
126+
127+
interface FastifyInstance {
128+
race(cb: FastifyRacing['onRequestClosed']): void
129+
race(opts: Omit<FastifyRacing, 'onRequestClosed'>): Promise<AbortEvent>
130+
race(): Promise<AbortEvent>
131+
}
132+
```
133+
134+
135+
> See [test](test/index.test.js) for more examples.

test/index.test-d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface FastifyRacing {
1616
declare module 'fastify' {
1717
interface FastifyInstance {
1818
race(cb: FastifyRacing['onRequestClosed']): void
19-
race(opts: FastifyRacing): Promise<AbortEvent>
19+
race(opts: Omit<FastifyRacing, 'onRequestClosed'>): Promise<AbortEvent>
2020
race(): Promise<AbortEvent>
2121
}
2222
}

0 commit comments

Comments
 (0)