fastify 5.8.3 → 5.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/Guides/Ecosystem.md +4 -0
- package/fastify.js +1 -1
- package/lib/request.js +13 -19
- package/lib/validation.js +1 -1
- package/package.json +4 -4
- package/test/request-port.test.js +72 -0
- package/test/schema-validation.test.js +21 -0
- package/test/trust-proxy.test.js +53 -0
- package/AGENTS.md +0 -290
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -225,6 +225,9 @@ section.
|
|
|
225
225
|
Fast sodium-based crypto for @mgcrea/fastify-session
|
|
226
226
|
- [`@mgcrea/pino-pretty-compact`](https://github.com/mgcrea/pino-pretty-compact)
|
|
227
227
|
A custom compact pino-base prettifier
|
|
228
|
+
- [`@pompelmi/fastify-plugin`](https://github.com/pompelmi/pompelmi/tree/main/packages/fastify-plugin)
|
|
229
|
+
In-process file upload scanning for Fastify with MIME/magic-byte
|
|
230
|
+
validation, ZIP bomb protection, size limits, and optional YARA.
|
|
228
231
|
- [`@pybot/fastify-autoload`](https://github.com/kunal097/fastify-autoload)
|
|
229
232
|
Plugin to generate routes automatically with valid json content
|
|
230
233
|
- [`@scalar/fastify-api-reference`](https://github.com/scalar/scalar/tree/main/integrations/fastify)
|
|
@@ -724,3 +727,4 @@ middlewares into Fastify plugins
|
|
|
724
727
|
Fastify plugin for Vite with Hot-module Replacement.
|
|
725
728
|
- [`vite-plugin-fastify-routes`](https://github.com/Vanilla-IceCream/vite-plugin-fastify-routes)
|
|
726
729
|
File-based routing for Fastify applications using Vite.
|
|
730
|
+
|
package/fastify.js
CHANGED
package/lib/request.js
CHANGED
|
@@ -47,16 +47,14 @@ function getTrustProxyFn (tp) {
|
|
|
47
47
|
}
|
|
48
48
|
if (typeof tp === 'number') {
|
|
49
49
|
// Support trusting hop count
|
|
50
|
-
return function (a, i) { return
|
|
50
|
+
return function (a, i) { return i < tp }
|
|
51
51
|
}
|
|
52
52
|
if (typeof tp === 'string') {
|
|
53
53
|
// Support comma-separated tps
|
|
54
54
|
const values = tp.split(',').map(it => it.trim())
|
|
55
|
-
|
|
56
|
-
return function (a, i) { return a != null && trust(a, i) }
|
|
55
|
+
return proxyAddr.compile(values)
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
return function (a, i) { return a != null && trust(a, i) }
|
|
57
|
+
return proxyAddr.compile(tp)
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
function buildRequest (R, trustProxy) {
|
|
@@ -119,7 +117,8 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
119
117
|
},
|
|
120
118
|
host: {
|
|
121
119
|
get () {
|
|
122
|
-
|
|
120
|
+
const socketAddr = this.raw.socket?.remoteAddress
|
|
121
|
+
if (this.headers['x-forwarded-host'] && socketAddr !== null && proxyFn(socketAddr, 0)) {
|
|
123
122
|
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
|
|
124
123
|
}
|
|
125
124
|
/**
|
|
@@ -133,7 +132,8 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
133
132
|
},
|
|
134
133
|
protocol: {
|
|
135
134
|
get () {
|
|
136
|
-
|
|
135
|
+
const socketAddr = this.raw.socket?.remoteAddress
|
|
136
|
+
if (this.headers['x-forwarded-proto'] && socketAddr !== null && proxyFn(socketAddr, 0)) {
|
|
137
137
|
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])
|
|
138
138
|
}
|
|
139
139
|
if (this.socket) {
|
|
@@ -255,19 +255,13 @@ Object.defineProperties(Request.prototype, {
|
|
|
255
255
|
},
|
|
256
256
|
port: {
|
|
257
257
|
get () {
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
const portReg = /(?<port>:\d+)$/
|
|
259
|
+
const host = this.headers.host ?? this.headers[':authority'] ?? ''
|
|
260
|
+
const matches = portReg.exec(host)
|
|
261
|
+
if (matches === null || matches[1] === undefined) {
|
|
262
|
+
return null
|
|
262
263
|
}
|
|
263
|
-
|
|
264
|
-
const host = (this.headers.host ?? this.headers[':authority'] ?? '')
|
|
265
|
-
const portFromHeader = parseInt(host.split(':').slice(-1)[0])
|
|
266
|
-
if (!isNaN(portFromHeader)) {
|
|
267
|
-
return portFromHeader
|
|
268
|
-
}
|
|
269
|
-
// fall back to null
|
|
270
|
-
return null
|
|
264
|
+
return parseInt(matches.groups.port.slice(1), 10)
|
|
271
265
|
}
|
|
272
266
|
},
|
|
273
267
|
protocol: {
|
package/lib/validation.js
CHANGED
|
@@ -269,7 +269,7 @@ function wrapValidationError (result, dataVar, schemaErrorFormatter) {
|
|
|
269
269
|
*/
|
|
270
270
|
function getEssenceMediaType (header) {
|
|
271
271
|
if (!header) return ''
|
|
272
|
-
return header.split(/[ ;]/, 1)[0].trim().toLowerCase()
|
|
272
|
+
return header.trimStart().split(/[ ;]/, 1)[0].trim().toLowerCase()
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.5",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"ajv-i18n": "^4.2.0",
|
|
178
178
|
"ajv-merge-patch": "^5.0.1",
|
|
179
179
|
"autocannon": "^8.0.0",
|
|
180
|
-
"borp": "^0.
|
|
180
|
+
"borp": "^1.0.0",
|
|
181
181
|
"branch-comparer": "^1.1.0",
|
|
182
182
|
"concurrently": "^9.1.2",
|
|
183
183
|
"cross-env": "^10.0.0",
|
|
@@ -190,14 +190,14 @@
|
|
|
190
190
|
"joi": "^18.0.1",
|
|
191
191
|
"json-schema-to-ts": "^3.0.1",
|
|
192
192
|
"JSONStream": "^1.3.5",
|
|
193
|
-
"markdownlint-cli2": "^0.
|
|
193
|
+
"markdownlint-cli2": "^0.22.0",
|
|
194
194
|
"neostandard": "^0.12.0",
|
|
195
195
|
"node-forge": "^1.3.1",
|
|
196
196
|
"proxyquire": "^2.1.3",
|
|
197
197
|
"split2": "^4.2.0",
|
|
198
198
|
"tsd": "^0.33.0",
|
|
199
199
|
"typebox": "^1.0.81",
|
|
200
|
-
"typescript": "~
|
|
200
|
+
"typescript": "~6.0.2",
|
|
201
201
|
"undici": "^7.11.0",
|
|
202
202
|
"vary": "^1.1.2",
|
|
203
203
|
"yup": "^1.4.0"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const test = require('node:test')
|
|
4
|
+
const Request = require('../lib/request')
|
|
5
|
+
|
|
6
|
+
test('.port parses port correctly', (t) => {
|
|
7
|
+
const fixtures = [
|
|
8
|
+
{
|
|
9
|
+
expected: 80,
|
|
10
|
+
req: {
|
|
11
|
+
headers: {
|
|
12
|
+
host: 'example.com:80'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
expected: 443,
|
|
18
|
+
req: {
|
|
19
|
+
headers: {
|
|
20
|
+
host: 'example.com:443'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
expected: 80,
|
|
26
|
+
req: {
|
|
27
|
+
headers: {
|
|
28
|
+
host: '[::1]:80'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
expected: 443,
|
|
34
|
+
req: {
|
|
35
|
+
headers: {
|
|
36
|
+
host: '[::1]:443'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
expected: null,
|
|
42
|
+
req: {
|
|
43
|
+
headers: {
|
|
44
|
+
host: '[::1]'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
expected: 80,
|
|
50
|
+
req: {
|
|
51
|
+
headers: {
|
|
52
|
+
':authority': '1.2.3.4:80'
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
expected: null,
|
|
58
|
+
req: {
|
|
59
|
+
headers: {}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
for (const fixture of fixtures) {
|
|
65
|
+
const req = new Request(1, {}, fixture.req, '', {}, {})
|
|
66
|
+
t.assert.equal(
|
|
67
|
+
req.port,
|
|
68
|
+
fixture.expected,
|
|
69
|
+
`${fixture.req.headers.host} should parse to ${fixture.expected}`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
@@ -1460,6 +1460,27 @@ test('Schema validation will not be bypass by different content type', async t =
|
|
|
1460
1460
|
t.assert.strictEqual(found.status, 400)
|
|
1461
1461
|
t.assert.strictEqual((await found.json()).code, 'FST_ERR_VALIDATION')
|
|
1462
1462
|
|
|
1463
|
+
let injected = await fastify.inject({
|
|
1464
|
+
method: 'POST',
|
|
1465
|
+
url: '/',
|
|
1466
|
+
headers: {
|
|
1467
|
+
'content-type': ' application/json'
|
|
1468
|
+
},
|
|
1469
|
+
payload: JSON.stringify({ invalid: 'string' })
|
|
1470
|
+
})
|
|
1471
|
+
t.assert.strictEqual(injected.statusCode, 400)
|
|
1472
|
+
t.assert.strictEqual(injected.json().code, 'FST_ERR_VALIDATION')
|
|
1473
|
+
|
|
1474
|
+
injected = await fastify.inject({
|
|
1475
|
+
method: 'POST',
|
|
1476
|
+
url: '/',
|
|
1477
|
+
headers: {
|
|
1478
|
+
'content-type': ' application/json'
|
|
1479
|
+
},
|
|
1480
|
+
payload: JSON.stringify({ foo: 'string' })
|
|
1481
|
+
})
|
|
1482
|
+
t.assert.strictEqual(injected.statusCode, 200)
|
|
1483
|
+
|
|
1463
1484
|
found = await fetch(address, {
|
|
1464
1485
|
method: 'POST',
|
|
1465
1486
|
url: '/',
|
package/test/trust-proxy.test.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const { test, before } = require('node:test')
|
|
4
4
|
const fastify = require('..')
|
|
5
5
|
const helper = require('./helper')
|
|
6
|
+
const Request = require('../lib/request')
|
|
7
|
+
const buildRequest = Request.buildRequest
|
|
6
8
|
|
|
7
9
|
const fetchForwardedRequest = async (fastifyServer, forHeader, path, protoHeader) => {
|
|
8
10
|
const headers = {
|
|
@@ -219,3 +221,54 @@ test('trust proxy reads forwarded headers from trusted connections', async t =>
|
|
|
219
221
|
}
|
|
220
222
|
})
|
|
221
223
|
})
|
|
224
|
+
|
|
225
|
+
test('trust proxy with number and undefined socket remoteAddress', t => {
|
|
226
|
+
t.plan(3)
|
|
227
|
+
|
|
228
|
+
// Test case for issue #6606: trustProxy: 1 with undefined/null socket.remoteAddress
|
|
229
|
+
// This simulates IISNode on Windows where socket.remoteAddress may be undefined
|
|
230
|
+
const headers = {
|
|
231
|
+
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
232
|
+
'x-forwarded-host': 'fastify.test',
|
|
233
|
+
'x-forwarded-proto': 'https'
|
|
234
|
+
}
|
|
235
|
+
// socket must exist but remoteAddress can be undefined
|
|
236
|
+
// This is what happens in IISNode with enableXFF="true"
|
|
237
|
+
const req = {
|
|
238
|
+
method: 'GET',
|
|
239
|
+
url: '/',
|
|
240
|
+
socket: { remoteAddress: undefined },
|
|
241
|
+
headers
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const TpRequest = buildRequest(Request, 1)
|
|
245
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
246
|
+
// Even with undefined socket.remoteAddress, req.ip should be populated from X-Forwarded-For
|
|
247
|
+
t.assert.ok(request.ip, 'ip is defined')
|
|
248
|
+
// With trustProxy: 1, we trust 1 hop from socket. Since socket.remoteAddress is undefined,
|
|
249
|
+
// the hop count check should skip it and we get 1.1.1.1 (the first trusted address from X-Forwarded-For)
|
|
250
|
+
t.assert.strictEqual(request.ip, '1.1.1.1', 'gets ip from x-forwarded-for')
|
|
251
|
+
// The host should also work correctly
|
|
252
|
+
t.assert.strictEqual(request.host, 'fastify.test', 'gets host from x-forwarded-host')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test('trust proxy with number and null socket remoteAddress', t => {
|
|
256
|
+
t.plan(2)
|
|
257
|
+
|
|
258
|
+
// Test case for trustProxy: 1 with null socket.remoteAddress
|
|
259
|
+
const headers = {
|
|
260
|
+
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
261
|
+
'x-forwarded-host': 'fastify.test'
|
|
262
|
+
}
|
|
263
|
+
const req = {
|
|
264
|
+
method: 'GET',
|
|
265
|
+
url: '/',
|
|
266
|
+
socket: { remoteAddress: null },
|
|
267
|
+
headers
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const TpRequest = buildRequest(Request, 1)
|
|
271
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
272
|
+
t.assert.ok(request.ip, 'ip is defined')
|
|
273
|
+
t.assert.strictEqual(request.ip, '1.1.1.1', 'gets ip from x-forwarded-for')
|
|
274
|
+
})
|
package/AGENTS.md
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
# AGENTS.md - Guide for AI Agents Working with Fastify
|
|
2
|
-
|
|
3
|
-
This document provides information and guidelines for AI agents (such as GitHub Copilot, Cursor, pi, or other AI coding assistants) working with the Fastify codebase.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
Fastify is a high-performance web framework for Node.js focused on:
|
|
8
|
-
- **Speed**: One of the fastest Node.js web frameworks
|
|
9
|
-
- **Extensibility**: Powerful plugin architecture with hooks and decorators
|
|
10
|
-
- **Schema-based**: JSON Schema validation and serialization
|
|
11
|
-
- **Developer experience**: Expressive API with minimal overhead
|
|
12
|
-
- **TypeScript support**: Full type definitions included
|
|
13
|
-
|
|
14
|
-
**Current Version**: 5.7.1 (main branch)
|
|
15
|
-
**Repository**: https://github.com/fastify/fastify
|
|
16
|
-
|
|
17
|
-
## Repository Structure
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
fastify/
|
|
21
|
-
├── docs/ # Documentation (Guides and Reference)
|
|
22
|
-
│ ├── Guides/ # Tutorials and how-to guides
|
|
23
|
-
│ └── Reference/ # API documentation
|
|
24
|
-
├── examples/ # Example applications and benchmarks
|
|
25
|
-
├── lib/ # Core library code
|
|
26
|
-
├── test/ # Test files
|
|
27
|
-
├── types/ # TypeScript type definitions
|
|
28
|
-
├── build/ # Build scripts
|
|
29
|
-
├── integration/ # Integration tests
|
|
30
|
-
├── fastify.js # Main entry point
|
|
31
|
-
├── fastify.d.ts # Main TypeScript definitions
|
|
32
|
-
└── package.json # Dependencies and scripts
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Key Files for Agents
|
|
36
|
-
|
|
37
|
-
### Core Files
|
|
38
|
-
- **`fastify.js`** - Main Fastify class and entry point
|
|
39
|
-
- **`fastify.d.ts`** - TypeScript type definitions (keep these in sync)
|
|
40
|
-
- **`lib/`** - All core functionality:
|
|
41
|
-
- `route.js` - Route handling
|
|
42
|
-
- `req-res.js` - Request and Reply objects
|
|
43
|
-
- `hooks.js` - Lifecycle hooks
|
|
44
|
-
- `plugin.js` - Plugin system
|
|
45
|
-
- `validation.js` - Schema validation
|
|
46
|
-
- `content-type-parser.js` - Body parsing
|
|
47
|
-
- `logger.js` - Pino logger integration
|
|
48
|
-
|
|
49
|
-
### Configuration Files
|
|
50
|
-
- **`package.json`** - Scripts, dependencies, and contributors
|
|
51
|
-
- **`eslint.config.js`** - Linting configuration (uses neostandard)
|
|
52
|
-
- **`.markdownlint-cli2.yaml`** - Markdown linting rules
|
|
53
|
-
|
|
54
|
-
### Documentation Files
|
|
55
|
-
- **`README.md`** - Project overview and quick start
|
|
56
|
-
- **`CONTRIBUTING.md`** - Contribution guidelines
|
|
57
|
-
- **`GOVERNANCE.md`** - Links to organization governance
|
|
58
|
-
- **`SECURITY.md`** - Security policy
|
|
59
|
-
- **`docs/Guides/Contributing.md`** - Detailed contributing guide
|
|
60
|
-
- **`docs/Guides/Style-Guide.md`** - Coding style conventions
|
|
61
|
-
|
|
62
|
-
## Testing Conventions
|
|
63
|
-
|
|
64
|
-
### Test Framework
|
|
65
|
-
Fastify uses **Borp** (a custom test runner) for testing.
|
|
66
|
-
|
|
67
|
-
### Test Structure
|
|
68
|
-
- Tests are in the **`test/`** directory
|
|
69
|
-
- Test files follow the pattern: `test/<module>.test.js`
|
|
70
|
-
- Integration tests are in **`integration/`** directory
|
|
71
|
-
|
|
72
|
-
### Running Tests
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Run all tests
|
|
76
|
-
npm test
|
|
77
|
-
|
|
78
|
-
# Run unit tests only
|
|
79
|
-
npm run unit
|
|
80
|
-
|
|
81
|
-
# Run tests with coverage
|
|
82
|
-
npm run coverage
|
|
83
|
-
|
|
84
|
-
# Run tests in watch mode
|
|
85
|
-
npm run test:watch
|
|
86
|
-
|
|
87
|
-
# Run TypeScript type tests
|
|
88
|
-
npm run test:typescript
|
|
89
|
-
|
|
90
|
-
# Run CI tests (minimal)
|
|
91
|
-
npm run test:ci
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Test Requirements
|
|
95
|
-
- **100% line coverage** is required for all changes (enforced by CI)
|
|
96
|
-
- Tests must pass on all supported Node.js versions
|
|
97
|
-
- TypeScript types must be tested (using `tsd`)
|
|
98
|
-
|
|
99
|
-
## Code Style and Conventions
|
|
100
|
-
|
|
101
|
-
### Linting
|
|
102
|
-
- Uses **Neostandard** JavaScript style guide
|
|
103
|
-
- ESLint is configured in `eslint.config.js`
|
|
104
|
-
- Run `npm run lint` to check code style
|
|
105
|
-
- Run `npm run lint:fix` to auto-fix issues
|
|
106
|
-
|
|
107
|
-
### Key Style Rules (from Style-Guide.md)
|
|
108
|
-
- Use `const` and `let`, never `var`
|
|
109
|
-
- Use arrow functions for callbacks
|
|
110
|
-
- Use async/await instead of promises
|
|
111
|
-
- Follow semicolon usage (neostandard enforces this)
|
|
112
|
-
- Use template literals for string interpolation
|
|
113
|
-
- Prefer functional methods (`map`, `filter`, `reduce`) over loops
|
|
114
|
-
- Error-first callback pattern for async operations where needed
|
|
115
|
-
|
|
116
|
-
### Naming Conventions
|
|
117
|
-
- **Files**: kebab-case (`content-type-parser.js`)
|
|
118
|
-
- **Variables**: camelCase
|
|
119
|
-
- **Constants**: UPPER_SNAKE_CASE
|
|
120
|
-
- **Classes**: PascalCase
|
|
121
|
-
- **Private methods**: prefixed with `_`
|
|
122
|
-
|
|
123
|
-
## Common Tasks
|
|
124
|
-
|
|
125
|
-
### Adding a New Feature
|
|
126
|
-
1. Implement the feature in `lib/`
|
|
127
|
-
2. Add tests in `test/`
|
|
128
|
-
3. Update TypeScript types in `fastify.d.ts` or `types/`
|
|
129
|
-
4. Update documentation in `docs/`
|
|
130
|
-
5. Run `npm test` to ensure all tests pass
|
|
131
|
-
6. Run `npm run lint` to check code style
|
|
132
|
-
7. Add changelog entry for release
|
|
133
|
-
|
|
134
|
-
### Fixing a Bug
|
|
135
|
-
1. Add a failing test case in `test/`
|
|
136
|
-
2. Fix the bug in `lib/`
|
|
137
|
-
3. Ensure all tests pass
|
|
138
|
-
4. Check if TypeScript types need updating
|
|
139
|
-
5. Update documentation if behavior changes
|
|
140
|
-
|
|
141
|
-
### Working with Plugins
|
|
142
|
-
- See `docs/Guides/Write-Plugin.md` for plugin authoring
|
|
143
|
-
- See `docs/Guides/Plugins-Guide.md` for plugin usage
|
|
144
|
-
- Plugin example: `lib/plugin.js`
|
|
145
|
-
|
|
146
|
-
## Architecture Highlights
|
|
147
|
-
|
|
148
|
-
### Core Components
|
|
149
|
-
|
|
150
|
-
1. **Server (`fastify.js`)**
|
|
151
|
-
- Main Fastify class
|
|
152
|
-
- Server initialization and configuration
|
|
153
|
-
- Plugin system integration (via `avvio`)
|
|
154
|
-
|
|
155
|
-
2. **Routing (`lib/route.js`)**
|
|
156
|
-
- Uses `find-my-way` for fast route matching
|
|
157
|
-
- Route registration and lookup
|
|
158
|
-
- Shorthand methods (get, post, put, delete, etc.)
|
|
159
|
-
|
|
160
|
-
3. **Request/Response (`lib/req-res.js`)**
|
|
161
|
-
- Request object extensions
|
|
162
|
-
- Reply object with fluent API
|
|
163
|
-
- Decorator support
|
|
164
|
-
|
|
165
|
-
4. **Hooks (`lib/hooks.js`)**
|
|
166
|
-
- Lifecycle hooks (onRequest, preHandler, etc.)
|
|
167
|
-
- Hook execution order and timing
|
|
168
|
-
|
|
169
|
-
5. **Validation (`lib/validation.js`)**
|
|
170
|
-
- JSON Schema validation via AJV
|
|
171
|
-
- Response serialization
|
|
172
|
-
- Built-in error serializer
|
|
173
|
-
|
|
174
|
-
6. **Content Type Parser (`lib/content-type-parser.js`)**
|
|
175
|
-
- Request body parsing
|
|
176
|
-
- Custom parser support
|
|
177
|
-
- JSON and other formats
|
|
178
|
-
|
|
179
|
-
### Plugin System
|
|
180
|
-
- Plugins are loaded asynchronously via `avvio`
|
|
181
|
-
- Supports encapsulation (scoped plugins)
|
|
182
|
-
- Hooks and decorators can be scoped
|
|
183
|
-
- See `lib/plugin.js` for implementation
|
|
184
|
-
|
|
185
|
-
## TypeScript Integration
|
|
186
|
-
|
|
187
|
-
- TypeScript definitions are in `fastify.d.ts` and `types/`
|
|
188
|
-
- Types must be tested with `tsd`
|
|
189
|
-
- Run `npm run test:typescript` to verify types
|
|
190
|
-
- Keep types in sync with JavaScript implementation
|
|
191
|
-
|
|
192
|
-
## Performance Considerations
|
|
193
|
-
|
|
194
|
-
Fastify prioritizes performance:
|
|
195
|
-
- **Routes**: Pre-compiled functions for fast matching
|
|
196
|
-
- **Validation**: Compiled JSON Schema validators
|
|
197
|
-
- **Serialization**: Compiled serializers (fast-json-stringify)
|
|
198
|
-
- **Logging**: Low-overhead Pino logger
|
|
199
|
-
- **Caching**: Route context caching with `toad-cache`
|
|
200
|
-
|
|
201
|
-
When making changes:
|
|
202
|
-
- Profile performance impact for hot paths
|
|
203
|
-
- Use benchmarks in `examples/benchmark/`
|
|
204
|
-
- Run `npm run benchmark` to measure
|
|
205
|
-
|
|
206
|
-
## Documentation Updates
|
|
207
|
-
|
|
208
|
-
Documentation is critical for Fastify. When changing behavior:
|
|
209
|
-
|
|
210
|
-
1. Update relevant docs in `docs/Reference/` for API changes
|
|
211
|
-
2. Update `docs/Guides/` for usage pattern changes
|
|
212
|
-
3. Check for broken links (CI validates this)
|
|
213
|
-
4. Update examples in `examples/` if needed
|
|
214
|
-
5. Run `npm run lint:markdown` to check docs
|
|
215
|
-
|
|
216
|
-
## Pre-commit Checks
|
|
217
|
-
|
|
218
|
-
Before submitting changes, ensure:
|
|
219
|
-
|
|
220
|
-
1. ✅ All tests pass: `npm test`
|
|
221
|
-
2. ✅ 100% coverage: `npm run coverage`
|
|
222
|
-
3. ✅ Linting passes: `npm run lint`
|
|
223
|
-
4. ✅ TypeScript types pass: `npm run test:typescript`
|
|
224
|
-
5. ✅ Markdown linting passes: `npm run lint:markdown`
|
|
225
|
-
6. ✅ Documentation is updated
|
|
226
|
-
7. ✅ Examples still work if affected
|
|
227
|
-
|
|
228
|
-
## Working with CI
|
|
229
|
-
|
|
230
|
-
Fastify uses GitHub Actions for CI. Workflows are in `.github/workflows/`:
|
|
231
|
-
- **`ci.yml`** - Main CI pipeline
|
|
232
|
-
- **`package-manager-ci.yml`** - Tests multiple package managers
|
|
233
|
-
- **`website.yml`** - Website deployment
|
|
234
|
-
|
|
235
|
-
## Agent-Specific Tips
|
|
236
|
-
|
|
237
|
-
### When Generating Code
|
|
238
|
-
1. Check existing patterns in `lib/` before creating new patterns
|
|
239
|
-
2. Follow the established error handling patterns
|
|
240
|
-
3. Use async/await consistently
|
|
241
|
-
4. Add appropriate hooks if extending lifecycle
|
|
242
|
-
5. Consider TypeScript types from the start
|
|
243
|
-
|
|
244
|
-
### When Refactoring
|
|
245
|
-
1. Ensure all tests still pass
|
|
246
|
-
2. Don't change public APIs without semver consideration
|
|
247
|
-
3. Update TypeScript definitions if signatures change
|
|
248
|
-
4. Check for deprecation needs
|
|
249
|
-
5. Update documentation for changed behavior
|
|
250
|
-
|
|
251
|
-
### When Analyzing Issues
|
|
252
|
-
1. Check `test/` for usage examples
|
|
253
|
-
2. Review relevant `docs/Reference/` files
|
|
254
|
-
3. Look at similar implementations in `lib/`
|
|
255
|
-
4. Consider the plugin system and encapsulation
|
|
256
|
-
5. Check hook timing and order
|
|
257
|
-
|
|
258
|
-
### Common Gotchas
|
|
259
|
-
- **Encapsulation**: Plugins are isolated - decorators don't leak
|
|
260
|
-
- **Hook order**: Hooks run in specific order (see docs/Reference/Hooks.md)
|
|
261
|
-
- **Async boot**: Server starts asynchronously - use `ready()` or `after()`
|
|
262
|
-
- **Error handling**: Use Fastify error classes from `@fastify/error`
|
|
263
|
-
- **Validation**: Schemas are compiled - changes require recompilation
|
|
264
|
-
|
|
265
|
-
## Key Dependencies
|
|
266
|
-
|
|
267
|
-
- **`avvio`** - Plugin loading and boot
|
|
268
|
-
- **`find-my-way`** - Fast HTTP router
|
|
269
|
-
- **`fast-json-stringify`** - Response serialization
|
|
270
|
-
- **`pino`** - Logging
|
|
271
|
-
- **`@fastify/ajv-compiler`** - JSON Schema validation
|
|
272
|
-
- **`light-my-request`** - HTTP injection for testing
|
|
273
|
-
|
|
274
|
-
## Contact and Resources
|
|
275
|
-
|
|
276
|
-
- **Documentation**: https://fastify.dev/
|
|
277
|
-
- **Discord**: https://discord.gg/fastify
|
|
278
|
-
- **GitHub Issues**: https://github.com/fastify/fastify/issues
|
|
279
|
-
- **GitHub Discussions**: https://github.com/fastify/fastify/discussions
|
|
280
|
-
- **Help**: https://github.com/fastify/help
|
|
281
|
-
|
|
282
|
-
## Version Information
|
|
283
|
-
|
|
284
|
-
- **Main branch**: Fastify v5
|
|
285
|
-
- **v4 branch**: https://github.com/fastify/fastify/tree/4.x
|
|
286
|
-
- **LTS Policy**: See `docs/Reference/LTS.md`
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
This document is maintained by the Fastify team. For questions or suggestions, please open an issue or discussion.
|