Skip to content

Commit

Permalink
Merge branch 'main' into (fastify#4439)-Access-handler-name-add-prope…
Browse files Browse the repository at this point in the history
…rties-to-req-route-options
  • Loading branch information
Eomm committed Mar 12, 2023
2 parents c0b4b34 + eafeed7 commit 81a87aa
Show file tree
Hide file tree
Showing 64 changed files with 1,765 additions and 534 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Expand Up @@ -66,7 +66,7 @@ jobs:
pull-requests: write
steps:
- name: Comment PR
uses: thollander/actions-comment-pull-request@v1
uses: thollander/actions-comment-pull-request@v2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
message: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/md-lint.yml
Expand Up @@ -37,7 +37,7 @@ jobs:
run: npm install --ignore-scripts

- name: Add Matcher
uses: xt0rted/markdownlint-problem-matcher@b643b0751c371f357690337d4549221347c0e1bc
uses: xt0rted/markdownlint-problem-matcher@98d94724052d20ca2e06c091f202e4c66c3c59fb

- name: Run Linter
run: ./node_modules/.bin/markdownlint-cli2
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/package-manager-ci.yml
Expand Up @@ -15,8 +15,8 @@ jobs:
strategy:
matrix:
# Maintenance and active LTS
node-version: [14, 16]
os: [ubuntu-18.04]
node-version: [14, 16, 18]
os: [ubuntu-latest]

steps:
- uses: actions/checkout@v3
Expand All @@ -43,8 +43,8 @@ jobs:
strategy:
matrix:
# Maintenance and active LTS
node-version: [14, 16]
os: [ubuntu-18.04]
node-version: [14, 16, 18]
os: [ubuntu-latest]

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -136,6 +136,7 @@ dist
.DS_Store

# lock files
bun.lockb
package-lock.json
pnpm-lock.yaml
yarn.lock
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2016-2022 The Fastify Team
Copyright (c) 2016-2023 The Fastify Team

The Fastify team members are listed at https://github.com/fastify/fastify#team
and in the README file.
Expand Down
8 changes: 5 additions & 3 deletions docs/Guides/Contributing.md
Expand Up @@ -162,9 +162,11 @@ the left sidebar. But wait! We are not quite done yet. There are a few more
baseline settings that should be set before VSCode is ready.

Press `cmd+shift+p` to bring up the VSCode command input prompt. Type `open
settings (json)` and then choose the same item from the filtered menu. This will
open a document that is the settings for the editor. Paste the following JSON
into this document, overwriting any text already present, and save it:
settings (json)`. Three [VSCode Setting](https://code.visualstudio.com/docs/getstarted/settings)
options will appear in the dropdown: Workspace, Default,
and User settings. We recommend selecting Default. This will open a document
that is the settings for the editor. Paste the following JSON into this
document, overwriting any text already present, and save it:

```json
{
Expand Down
170 changes: 170 additions & 0 deletions docs/Guides/Detecting-When-Clients-Abort.md
@@ -0,0 +1,170 @@
<h1 align="center">Fastify</h1>

# Detecting When Clients Abort

## Introduction

Fastify provides request events to trigger at certain points in a request's
lifecycle. However, there isn't a built-in mechanism to
detect unintentional client disconnection scenarios such as when the client's
internet connection is interrupted. This guide covers methods to detect if
and when a client intentionally aborts a request.

Keep in mind, Fastify's `clientErrorHandler` is not designed to detect when a
client aborts a request. This works in the same way as the standard Node HTTP
module, which triggers the `clientError` event when there is a bad request or
exceedingly large header data. When a client aborts a request, there is no
error on the socket and the `clientErrorHandler` will not be triggered.

## Solution

### Overview

The proposed solution is a possible way of detecting when a client
intentionally aborts a request, such as when a browser is closed or the HTTP
request is aborted from your client application. If there is an error in your
application code that results in the server crashing, you may require
additional logic to avoid a false abort detection.

The goal here is to detect when a client intentionally aborts a connection
so your application logic can proceed accordingly. This can be useful for
logging purposes or halting business logic.

### Hands-on

Say we have the following base server set up:

```js
import Fastify from 'fastify';

const sleep = async (time) => {
return await new Promise(resolve => setTimeout(resolve, time || 1000));
}

const app = Fastify({
logger: {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
})

app.addHook('onRequest', async (request, reply) => {
request.raw.on('close', () => {
if (request.raw.aborted) {
app.log.info('request closed')
}
})
})

app.get('/', async (request, reply) => {
await sleep(3000)
reply.code(200).send({ ok: true })
})

const start = async () => {
try {
await app.listen({ port: 3000 })
} catch (err) {
app.log.error(err)
process.exit(1)
}
}

start()
```

Our code is setting up a Fastify server which includes the following
functionality:

- Accepting requests at http://localhost:3000, with a 3 second delayed response
of `{ ok: true }`.
- An onRequest hook that triggers when every request is received.
- Logic that triggers in the hook when the request is closed.
- Logging that occurs when the closed request property `aborted` is true.

In the request close event, you should examine the diff between a successful
request and one aborted by the client to determine the best property for your
use case. You can find details about request properties in the
[NodeJS documentation](https://nodejs.org/api/http.html).

You can also perform this logic outside of a hook, directly in a specific route.

```js
app.get('/', async (request, reply) => {
request.raw.on('close', () => {
if (request.raw.aborted) {
app.log.info('request closed')
}
})
await sleep(3000)
reply.code(200).send({ ok: true })
})
```

At any point in your business logic, you can check if the request has been
aborted and perform alternative actions.

```js
app.get('/', async (request, reply) => {
await sleep(3000)
if (request.raw.aborted) {
// do something here
}
await sleep(3000)
reply.code(200).send({ ok: true })
})
```

A benefit to adding this in your application code is that you can log Fastify
details such as the reqId, which may be unavailable in lower-level code that
only has access to the raw request information.

### Testing

To test this functionality you can use an app like Postman and cancel your
request within 3 seconds. Alternatively, you can use Node to send an HTTP
request with logic to abort the request before 3 seconds. Example:

```js
const controller = new AbortController();
const signal = controller.signal;

(async () => {
try {
const response = await fetch('http://localhost:3000', { signal });
const body = await response.text();
console.log(body);
} catch (error) {
console.error(error);
}
})();

setTimeout(() => {
controller.abort()
}, 1000);
```

With either approach, you should see the Fastify log appear at the moment the
request is aborted.

## Conclusion

Specifics of the implementation will vary from one problem to another, but the
main goal of this guide was to show a very specific use case of an issue that
could be solved within Fastify's ecosystem.

You can listen to the request close event and determine if the request was
aborted or if it was successfully delivered. You can implement this solution
in an onRequest hook or directly in an individual route.

This approach will not trigger in the event of internet disruption, and such
detection would require additional business logic. If you have flawed backend
application logic that results in a server crash, then you could trigger a
false detection. The `clientErrorHandler`, either by default or with custom
logic, is not intended to handle this scenario and will not trigger when the
client aborts a request.
29 changes: 26 additions & 3 deletions docs/Guides/Ecosystem.md
Expand Up @@ -140,6 +140,8 @@ section.

- [`@applicazza/fastify-nextjs`](https://github.com/applicazza/fastify-nextjs)
Alternate Fastify and Next.js integration.
- [`@clerk/fastify`](https://github.com/clerkinc/javascript/tree/main/packages/fastify)
Add authentication and user management to your Fastify application with Clerk.
- [`@coobaha/typed-fastify`](https://github.com/Coobaha/typed-fastify) Strongly
typed routes with a runtime validation using JSON schema generated from types.
- [`@dnlup/fastify-doc`](https://github.com/dnlup/fastify-doc) A plugin for
Expand Down Expand Up @@ -201,6 +203,8 @@ section.
using Fastify without the need of consuming a port on Electron apps.
- [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for
waterline. Decorates Fastify with waterline models.
- [`fastify-204`](https://github.com/Shiva127/fastify-204) Fastify plugin that
return 204 status on empty response.
- [`fastify-405`](https://github.com/Eomm/fastify-405) Fastify plugin that adds
405 HTTP status to your routes
- [`fastify-allow`](https://github.com/mattbishop/fastify-allow) Fastify plugin
Expand Down Expand Up @@ -266,6 +270,10 @@ section.
- [`fastify-cloudevents`](https://github.com/smartiniOnGitHub/fastify-cloudevents)
Fastify plugin to generate and forward Fastify events in the Cloudevents
format.
- [`fastify-cloudinary`](https://github.com/Vanilla-IceCream/fastify-cloudinary)
The Cloudinary Fastify SDK allows you to quickly and easily integrate your
application with Cloudinary. Effortlessly optimize and transform your cloud's
assets.
- [`fastify-cockroachdb`](https://github.com/alex-ppg/fastify-cockroachdb)
Fastify plugin to connect to a CockroachDB PostgreSQL instance via the
Sequelize ORM.
Expand All @@ -280,6 +288,9 @@ section.
functions.
- [`fastify-decorators`](https://github.com/L2jLiga/fastify-decorators) Fastify
plugin that provides the set of TypeScript decorators.
- [`fastify-delay-request`](https://github.com/climba03003/fastify-delay-request)
Fastify plugin that allows requests to be delayed whilst a task the response is
dependent on is run, such as a resource intensive process.
- [`fastify-disablecache`](https://github.com/Fdawgs/fastify-disablecache)
Fastify plugin to disable client-side caching, inspired by
[nocache](https://github.com/helmetjs/nocache).
Expand Down Expand Up @@ -366,6 +377,11 @@ section.
- [`fastify-influxdb`](https://github.com/alex-ppg/fastify-influxdb) Fastify
InfluxDB plugin connecting to an InfluxDB instance via the Influx default
package.
- [`fastify-ip`](https://github.com/metcoder95/fastify-ip) A plugin
for Fastify that allows you to infer a request ID by a
given set of custom Request headers.
- [`fastify-json-to-xml`](https://github.com/Fdawgs/fastify-json-to-xml) Fastify
plugin to serialize JSON responses into XML.
- [`fastify-jwt-authz`](https://github.com/Ethan-Arrowood/fastify-jwt-authz) JWT
user scope verifier.
- [`fastify-jwt-webapp`](https://github.com/charlesread/fastify-jwt-webapp) JWT
Expand All @@ -384,7 +400,7 @@ section.
Fastify plugin to parse request language.
- [`fastify-lcache`](https://github.com/denbon05/fastify-lcache)
Lightweight cache plugin
- [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
- [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
A simple plugin for Fastify list all available routes.
- [`fastify-loader`](https://github.com/TheNoim/fastify-loader) Load routes from
a directory and inject the Fastify instance in each file.
Expand Down Expand Up @@ -474,6 +490,8 @@ section.
- [`fastify-postgraphile`](https://github.com/alemagio/fastify-postgraphile)
Plugin to integrate [PostGraphile](https://www.graphile.org/postgraphile/) in
a Fastify project.
- [`fastify-postgres-dot-js`](https://github.com/kylerush/fastify-postgresjs) Fastify
PostgreSQL connection plugin that uses [Postgres.js](https://github.com/porsager/postgres).
- [`fastify-prettier`](https://github.com/hsynlms/fastify-prettier) A Fastify
plugin that uses [prettier](https://github.com/prettier/prettier) under the
hood to beautify outgoing responses and/or other things in the Fastify server.
Expand Down Expand Up @@ -532,9 +550,11 @@ section.
- [`fastify-server-session`](https://github.com/jsumners/fastify-server-session)
A session plugin with support for arbitrary backing caches via
`fastify-caching`.
- [`fastify-shared-schema`](https://github.com/Adibla/fastify-shared-schema) Plugin
for sharing schemas between different routes.
- [`fastify-slonik`](https://github.com/Unbuttun/fastify-slonik) Fastify Slonik
plugin, with this you can use slonik in every part of your server.
- [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
- [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
to delay the response from the server.
- [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io) a
Socket.io plugin for Fastify.
Expand Down Expand Up @@ -585,6 +605,8 @@ section.
should use.
- [`fastify-wamp-router`](https://github.com/lependu/fastify-wamp-router) Web
Application Messaging Protocol router for Fastify.
- [`fastify-web-response`](https://github.com/erfanium/fastify-web-response)
Enables returning web streams objects `Response` and `ReadableStream` in routes.
- [`fastify-webpack-hmr`](https://github.com/lependu/fastify-webpack-hmr)
Webpack hot module reloading plugin for Fastify.
- [`fastify-webpack-hot`](https://github.com/gajus/fastify-webpack-hot) Webpack
Expand Down Expand Up @@ -621,11 +643,12 @@ section.
and lightweight Sequelize plugin for Fastify.
- [`typeorm-fastify-plugin`](https://github.com/jclemens24/fastify-typeorm) A simple
and updated Typeorm plugin for use with Fastify.

#### [Community Tools](#community-tools)
- [`@fastify-userland/workflows`](https://github.com/fastify-userland/workflows)
Reusable workflows for use in the Fastify plugin
- [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
generator by directory structure.
- [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building
Fastify APIs using decorators and convert Typescript interface to JSON Schema.
- [`simple-tjscli`](https://github.com/imjuni/simple-tjscli) CLI tool to
generate JSON Schema from TypeScript interfaces.
2 changes: 2 additions & 0 deletions docs/Guides/Index.md
Expand Up @@ -15,6 +15,8 @@ This table of contents is in alphabetical order.
met in your application. This guide focuses on solving the problem using
[`Hooks`](../Reference/Hooks.md), [`Decorators`](../Reference/Decorators.md),
and [`Plugins`](../Reference/Plugins.md).
+ [Detecting When Clients Abort](./Detecting-When-Clients-Abort.md): A
practical guide on detecting if and when a client aborts a request.
+ [Ecosystem](./Ecosystem.md): Lists all core plugins and many known community
plugins.
+ [Fluent Schema](./Fluent-Schema.md): Shows how writing JSON Schema can be
Expand Down
7 changes: 6 additions & 1 deletion docs/Guides/Migration-Guide-V4.md
Expand Up @@ -39,6 +39,11 @@ const res = await fastify.inject('/encapsulated')
console.log(res.json().message) // 'wrapped'
```

>The root error handler is Fastify’s generic error handler.
>This error handler will use the headers and status code in the Error object,
>if they exist. **The headers and status code will not be automatically set if
>a custom error handler is provided**.
### Removed `app.use()` ([#3506](https://github.com/fastify/fastify/pull/3506))

With v4 of Fastify, `app.use()` has been removed and the use of middleware is
Expand Down Expand Up @@ -116,7 +121,7 @@ As a result, if you specify an `onRoute` hook in a plugin you should now either:

Into this:
```js
await fastify.register((instance, opts) => {
await fastify.register((instance, opts, done) => {
instance.addHook('onRoute', (routeOptions) => {
const { path, method } = routeOptions;
console.log({ path, method });
Expand Down

0 comments on commit 81a87aa

Please sign in to comment.