fastify 5.7.1 → 5.7.3
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/AGENTS.md +290 -0
- package/LICENSE +1 -4
- package/SECURITY.md +4 -3
- package/SPONSORS.md +2 -2
- package/docs/Guides/Ecosystem.md +2 -1
- package/docs/Guides/Plugins-Guide.md +8 -8
- package/docs/Reference/Reply.md +14 -3
- package/docs/Reference/Server.md +6 -2
- package/docs/Reference/Validation-and-Serialization.md +15 -6
- package/fastify.js +1 -1
- package/lib/content-type-parser.js +40 -30
- package/lib/content-type.js +152 -0
- package/lib/error-serializer.js +24 -10
- package/lib/handle-request.js +12 -4
- package/lib/reply.js +15 -1
- package/package.json +2 -2
- package/test/content-parser.test.js +21 -39
- package/test/content-type.test.js +102 -1
- package/test/custom-parser.0.test.js +3 -3
- package/test/custom-parser.1.test.js +0 -35
- package/test/custom-parser.3.test.js +1 -1
- package/test/schema-validation.test.js +66 -33
- package/test/web-api.test.js +73 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
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.
|
package/LICENSE
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2016-present The Fastify Team
|
|
4
|
-
|
|
5
|
-
The Fastify team members are listed at https://github.com/fastify/fastify#team
|
|
6
|
-
and in the README file.
|
|
3
|
+
Copyright (c) 2016-present The Fastify Team (members are listed in the README file)
|
|
7
4
|
|
|
8
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/SECURITY.md
CHANGED
|
@@ -46,10 +46,11 @@ explicitly enabled via configuration options
|
|
|
46
46
|
## Reporting vulnerabilities
|
|
47
47
|
|
|
48
48
|
Individuals who find potential vulnerabilities in Fastify are invited to
|
|
49
|
-
complete a vulnerability report via the
|
|
49
|
+
complete a vulnerability report via the GitHub Security page:
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
https://github.com/fastify/fastify/security/advisories/new
|
|
52
|
+
|
|
53
|
+
Note: Our [HackerOne](https://hackerone.com/fastify) program is now closed.
|
|
53
54
|
|
|
54
55
|
### Strict measures when reporting vulnerabilities
|
|
55
56
|
|
package/SPONSORS.md
CHANGED
|
@@ -9,7 +9,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
|
|
|
9
9
|
|
|
10
10
|
## Tier 4
|
|
11
11
|
|
|
12
|
-
- [SerpApi](
|
|
12
|
+
- [SerpApi](https://serpapi.com/?utm_source=fastify)
|
|
13
13
|
|
|
14
14
|
## Tier 3
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
|
|
|
17
17
|
- [Val Town, Inc.](https://opencollective.com/valtown)
|
|
18
18
|
- [Handsontable - JavaScript Data Grid](https://handsontable.com/docs/react-data-grid/?utm_source=Fastify_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024)
|
|
19
19
|
- [Lokalise - A Localization and Translation Software Tool](https://lokalise.com/?utm_source=Fastify_GH&utm_medium=sponsorship)
|
|
20
|
-
- [
|
|
20
|
+
- [TestMu AI](https://www.testmu.ai/)
|
|
21
21
|
|
|
22
22
|
## Tier 2
|
|
23
23
|
|
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -427,6 +427,8 @@ section.
|
|
|
427
427
|
context to take place per API call within the Fastify lifecycle of calls.
|
|
428
428
|
- [`fastify-http-errors-enhanced`](https://github.com/ShogunPanda/fastify-http-errors-enhanced)
|
|
429
429
|
An error handling plugin for Fastify that uses enhanced HTTP errors.
|
|
430
|
+
- [`fastify-http-exceptions`](https://github.com/bhouston/fastify-http-exceptions)
|
|
431
|
+
Typed HTTP status exceptions which are automatically converted into Fastify responses.
|
|
430
432
|
- [`fastify-http2https`](https://github.com/lolo32/fastify-http2https) Redirect
|
|
431
433
|
HTTP requests to HTTPS, both using the same port number, or different response
|
|
432
434
|
on HTTP and HTTPS.
|
|
@@ -746,7 +748,6 @@ middlewares into Fastify plugins
|
|
|
746
748
|
- [`typeorm-fastify-plugin`](https://github.com/jclemens24/fastify-typeorm) A simple
|
|
747
749
|
and updated Typeorm plugin for use with Fastify.
|
|
748
750
|
|
|
749
|
-
|
|
750
751
|
#### [Community Tools](#community-tools)
|
|
751
752
|
|
|
752
753
|
- [`@fastify-userland/workflows`](https://github.com/fastify-userland/workflows)
|
|
@@ -204,12 +204,12 @@ of an *arrow function expression*.
|
|
|
204
204
|
|
|
205
205
|
You can do the same for the `request` object:
|
|
206
206
|
```js
|
|
207
|
-
fastify.decorate('
|
|
208
|
-
return req.headers[header
|
|
207
|
+
fastify.decorate('getBoolHeader', (req, name) => {
|
|
208
|
+
return req.headers[name] ?? false // We return `false` if header is missing
|
|
209
209
|
})
|
|
210
210
|
|
|
211
211
|
fastify.addHook('preHandler', (request, reply, done) => {
|
|
212
|
-
request.isHappy = fastify.
|
|
212
|
+
request.isHappy = fastify.getBoolHeader(request, 'happy')
|
|
213
213
|
done()
|
|
214
214
|
})
|
|
215
215
|
|
|
@@ -219,14 +219,14 @@ fastify.get('/happiness', (request, reply) => {
|
|
|
219
219
|
```
|
|
220
220
|
Again, it works, but it can be much better!
|
|
221
221
|
```js
|
|
222
|
-
fastify.decorateRequest('
|
|
223
|
-
this.isHappy = this.headers[
|
|
222
|
+
fastify.decorateRequest('setBoolHeader', function (name) {
|
|
223
|
+
this.isHappy = this.headers[name] ?? false
|
|
224
224
|
})
|
|
225
225
|
|
|
226
226
|
fastify.decorateRequest('isHappy', false) // This will be added to the Request object prototype, yay speed!
|
|
227
227
|
|
|
228
228
|
fastify.addHook('preHandler', (request, reply, done) => {
|
|
229
|
-
request.
|
|
229
|
+
request.setBoolHeader('happy')
|
|
230
230
|
done()
|
|
231
231
|
})
|
|
232
232
|
|
|
@@ -337,11 +337,11 @@ fastify.register((instance, opts, done) => {
|
|
|
337
337
|
}
|
|
338
338
|
})
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
instance.get('/plugin1', {config: {useUtil: true}}, (request, reply) => {
|
|
341
341
|
reply.send(request)
|
|
342
342
|
})
|
|
343
343
|
|
|
344
|
-
|
|
344
|
+
instance.get('/plugin2', (request, reply) => {
|
|
345
345
|
reply.send(request)
|
|
346
346
|
})
|
|
347
347
|
|
package/docs/Reference/Reply.md
CHANGED
|
@@ -671,9 +671,20 @@ fastify.get('/json', options, function (request, reply) {
|
|
|
671
671
|
If you pass a string to `send` without a `Content-Type`, it will be sent as
|
|
672
672
|
`text/plain; charset=utf-8`. If you set the `Content-Type` header and pass a
|
|
673
673
|
string to `send`, it will be serialized with the custom serializer if one is
|
|
674
|
-
set, otherwise, it will be sent unmodified
|
|
675
|
-
|
|
676
|
-
|
|
674
|
+
set, otherwise, it will be sent unmodified.
|
|
675
|
+
|
|
676
|
+
> **Note:** Even when the `Content-Type` header is set to `application/json`,
|
|
677
|
+
> strings are sent unmodified by default. To serialize a string as JSON, you
|
|
678
|
+
> must set a custom serializer:
|
|
679
|
+
|
|
680
|
+
```js
|
|
681
|
+
fastify.get('/json-string', async function (request, reply) {
|
|
682
|
+
reply
|
|
683
|
+
.type('application/json; charset=utf-8')
|
|
684
|
+
.serializer(JSON.stringify)
|
|
685
|
+
.send('Hello') // Returns "Hello" (JSON-encoded string)
|
|
686
|
+
})
|
|
687
|
+
```
|
|
677
688
|
```js
|
|
678
689
|
fastify.get('/json', options, function (request, reply) {
|
|
679
690
|
reply.send('plain string')
|
package/docs/Reference/Server.md
CHANGED
|
@@ -534,7 +534,9 @@ recommend using a custom parser to convert only the keys to lowercase.
|
|
|
534
534
|
```js
|
|
535
535
|
const qs = require('qs')
|
|
536
536
|
const fastify = require('fastify')({
|
|
537
|
-
|
|
537
|
+
routerOptions: {
|
|
538
|
+
querystringParser: str => qs.parse(str)
|
|
539
|
+
}
|
|
538
540
|
})
|
|
539
541
|
```
|
|
540
542
|
|
|
@@ -544,7 +546,9 @@ like the example below for case insensitive keys and values:
|
|
|
544
546
|
```js
|
|
545
547
|
const querystring = require('fast-querystring')
|
|
546
548
|
const fastify = require('fastify')({
|
|
547
|
-
|
|
549
|
+
routerOptions: {
|
|
550
|
+
querystringParser: str => querystring.parse(str.toLowerCase())
|
|
551
|
+
}
|
|
548
552
|
})
|
|
549
553
|
```
|
|
550
554
|
|
|
@@ -675,9 +675,12 @@ const schema = {
|
|
|
675
675
|
content: {
|
|
676
676
|
'application/json': {
|
|
677
677
|
schema: {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
678
|
+
type: 'object',
|
|
679
|
+
properties: {
|
|
680
|
+
name: { type: 'string' },
|
|
681
|
+
image: { type: 'string' },
|
|
682
|
+
address: { type: 'string' }
|
|
683
|
+
}
|
|
681
684
|
}
|
|
682
685
|
},
|
|
683
686
|
'application/vnd.v1+json': {
|
|
@@ -692,8 +695,11 @@ const schema = {
|
|
|
692
695
|
content: {
|
|
693
696
|
'application/vnd.v2+json': {
|
|
694
697
|
schema: {
|
|
695
|
-
|
|
696
|
-
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
fullName: { type: 'string' },
|
|
701
|
+
phone: { type: 'string' }
|
|
702
|
+
}
|
|
697
703
|
}
|
|
698
704
|
}
|
|
699
705
|
}
|
|
@@ -703,7 +709,10 @@ const schema = {
|
|
|
703
709
|
// */* is match-all content-type
|
|
704
710
|
'*/*': {
|
|
705
711
|
schema: {
|
|
706
|
-
|
|
712
|
+
type: 'object',
|
|
713
|
+
properties: {
|
|
714
|
+
desc: { type: 'string' }
|
|
715
|
+
}
|
|
707
716
|
}
|
|
708
717
|
}
|
|
709
718
|
}
|
package/fastify.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { AsyncResource } = require('node:async_hooks')
|
|
4
4
|
const { FifoMap: Fifo } = require('toad-cache')
|
|
5
5
|
const { parse: secureJsonParse } = require('secure-json-parse')
|
|
6
|
+
const ContentType = require('./content-type')
|
|
6
7
|
const {
|
|
7
8
|
kDefaultJsonParse,
|
|
8
9
|
kContentTypeParser,
|
|
@@ -75,8 +76,13 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
|
|
|
75
76
|
this.customParsers.set('', parser)
|
|
76
77
|
} else {
|
|
77
78
|
if (contentTypeIsString) {
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const ct = new ContentType(contentType)
|
|
80
|
+
if (ct.isValid === false) {
|
|
81
|
+
throw new FST_ERR_CTP_INVALID_TYPE()
|
|
82
|
+
}
|
|
83
|
+
const normalizedContentType = ct.toString()
|
|
84
|
+
this.parserList.unshift(normalizedContentType)
|
|
85
|
+
this.customParsers.set(normalizedContentType, parser)
|
|
80
86
|
} else {
|
|
81
87
|
validateRegExp(contentType)
|
|
82
88
|
this.parserRegExpList.unshift(contentType)
|
|
@@ -87,7 +93,7 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
|
|
|
87
93
|
|
|
88
94
|
ContentTypeParser.prototype.hasParser = function (contentType) {
|
|
89
95
|
if (typeof contentType === 'string') {
|
|
90
|
-
contentType = contentType
|
|
96
|
+
contentType = new ContentType(contentType).toString()
|
|
91
97
|
} else {
|
|
92
98
|
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
|
93
99
|
contentType = contentType.toString()
|
|
@@ -97,45 +103,49 @@ ContentTypeParser.prototype.hasParser = function (contentType) {
|
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
100
|
-
if (contentType === '
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
if (typeof contentType === 'string') {
|
|
107
|
+
const ct = new ContentType(contentType).toString()
|
|
108
|
+
if (contentType === 'application/json' && this.customParsers.has(contentType)) {
|
|
109
|
+
return this.customParsers.get(ct).fn !== this[kDefaultJsonParse]
|
|
110
|
+
}
|
|
111
|
+
if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
|
|
112
|
+
return this.customParsers.get(ct).fn !== defaultPlainTextParser
|
|
113
|
+
}
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
return this.hasParser(contentType)
|
|
108
117
|
}
|
|
109
118
|
|
|
110
119
|
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
if (typeof contentType === 'string') {
|
|
121
|
+
contentType = new ContentType(contentType)
|
|
122
|
+
}
|
|
123
|
+
const ct = contentType.toString()
|
|
124
|
+
|
|
125
|
+
let parser = this.cache.get(ct)
|
|
114
126
|
if (parser !== undefined) return parser
|
|
127
|
+
parser = this.customParsers.get(ct)
|
|
128
|
+
if (parser !== undefined) {
|
|
129
|
+
this.cache.set(ct, parser)
|
|
130
|
+
return parser
|
|
131
|
+
}
|
|
115
132
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
caseInsensitiveContentType.charCodeAt(parserListItem.length) === 9 /* `\t` */
|
|
126
|
-
)
|
|
127
|
-
) {
|
|
128
|
-
parser = this.customParsers.get(parserListItem)
|
|
129
|
-
this.cache.set(contentType, parser)
|
|
130
|
-
return parser
|
|
131
|
-
}
|
|
133
|
+
// We have conflicting desires across our test suite. In some cases, we
|
|
134
|
+
// expect to get a parser by just passing the media-type. In others, we expect
|
|
135
|
+
// to get a parser registered under the media-type while also providing
|
|
136
|
+
// parameters. And in yet others, we expect to register a parser under the
|
|
137
|
+
// media-type and have it apply to any request with a header that starts
|
|
138
|
+
// with that type.
|
|
139
|
+
parser = this.customParsers.get(contentType.mediaType)
|
|
140
|
+
if (parser !== undefined) {
|
|
141
|
+
return parser
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
for (let j = 0; j !== this.parserRegExpList.length; ++j) {
|
|
135
145
|
const parserRegExp = this.parserRegExpList[j]
|
|
136
|
-
if (parserRegExp.test(
|
|
146
|
+
if (parserRegExp.test(ct)) {
|
|
137
147
|
parser = this.customParsers.get(parserRegExp.toString())
|
|
138
|
-
this.cache.set(
|
|
148
|
+
this.cache.set(ct, parser)
|
|
139
149
|
return parser
|
|
140
150
|
}
|
|
141
151
|
}
|
|
@@ -154,7 +164,7 @@ ContentTypeParser.prototype.remove = function (contentType) {
|
|
|
154
164
|
let parsers
|
|
155
165
|
|
|
156
166
|
if (typeof contentType === 'string') {
|
|
157
|
-
contentType = contentType
|
|
167
|
+
contentType = new ContentType(contentType).toString()
|
|
158
168
|
parsers = this.parserList
|
|
159
169
|
} else {
|
|
160
170
|
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|