express-fast-json-stringify 1.2.8 → 1.3.0
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/CHANGELOG.md +13 -0
- package/LICENSE +21 -21
- package/README.md +274 -113
- package/build/main/index.d.ts +2 -0
- package/build/main/index.js +2 -1
- package/build/main/lib/middleware.d.ts +24 -6
- package/build/main/lib/middleware.js +29 -14
- package/build/main/lib/openapi.d.ts +87 -0
- package/build/main/lib/openapi.js +166 -0
- package/build/main/lib/override.d.ts +17 -0
- package/build/main/lib/override.js +49 -0
- package/build/main/lib/send.d.ts +10 -0
- package/build/main/lib/send.js +46 -0
- package/build/module/index.d.ts +2 -0
- package/build/module/index.js +2 -1
- package/build/module/lib/middleware.d.ts +24 -6
- package/build/module/lib/middleware.js +18 -14
- package/build/module/lib/openapi.d.ts +87 -0
- package/build/module/lib/openapi.js +146 -0
- package/build/module/lib/override.d.ts +17 -0
- package/build/module/lib/override.js +44 -0
- package/build/module/lib/send.d.ts +10 -0
- package/build/module/lib/send.js +41 -0
- package/package.json +59 -80
- package/build/main/index.spec.d.ts +0 -1
- package/build/main/index.spec.js +0 -66
- package/build/module/index.spec.d.ts +0 -1
- package/build/module/index.spec.js +0 -61
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.0](https://github.com/nigrosimone/express-fast-json-stringify/compare/v1.2.9...v1.3.0) (2026-07-26)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* derive the schema from an OpenAPI or Swagger document ([5ce6622](https://github.com/nigrosimone/express-fast-json-stringify/commit/5ce6622fd216df1074e844c254bef0c7731205cd))
|
|
8
|
+
* opt in override of res.json, so adoption needs no call site changes ([bcfda45](https://github.com/nigrosimone/express-fast-json-stringify/commit/bcfda4579ac8380f53c525b5cadcbc177e835b9c))
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* align res.fastJson with res.json HTTP semantics; add CI and vitest ([f2c8558](https://github.com/nigrosimone/express-fast-json-stringify/commit/f2c85584c105ce24c6c7327382b6b80022aade10))
|
|
13
|
+
|
|
3
14
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
15
|
|
|
16
|
+
### [1.2.9](https://github.com/nigrosimone/express-fast-json-stringify/compare/v1.2.8...v1.2.9) (2026-01-18)
|
|
17
|
+
|
|
5
18
|
### [1.2.8](https://github.com/nigrosimone/express-fast-json-stringify/compare/v1.2.6...v1.2.8) (2024-09-29)
|
|
6
19
|
|
|
7
20
|
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2019 Nigro Simone
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Nigro Simone
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,113 +1,274 @@
|
|
|
1
|
-
JSON serialization is a critical task in web development, particularly for applications built with Node.js and Express.js. While the native JSON serialization in Node.js (`JSON.stringify()`) is straightforward and convenient, it can become a performance bottleneck, especially under heavy load. This
|
|
2
|
-
|
|
3
|
-
## What is fast-json-stringify?
|
|
4
|
-
|
|
5
|
-
`fast-json-stringify` is a JSON serialization library developed by the Fastify team. It
|
|
6
|
-
|
|
7
|
-
## Introducing express-fast-json-stringify
|
|
8
|
-
|
|
9
|
-
`express-fast-json-stringify` is an npm package that brings the performance benefits of `fast-json-stringify` to Express.js applications. By integrating this package, you can achieve faster JSON serialization, thus improving the overall performance of your application.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
First, install the `express-fast-json-stringify` package:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
npm install express-fast-json-stringify
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
1
|
+
JSON serialization is a critical task in web development, particularly for applications built with Node.js and Express.js. While the native JSON serialization in Node.js (`JSON.stringify()`) is straightforward and convenient, it can become a performance bottleneck, especially under heavy load. This documentation introduces [express-fast-json-stringify](https://www.npmjs.com/package/express-fast-json-stringify), a custom middleware package that leverages [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) to significantly boost JSON serialization performance in Express applications.
|
|
2
|
+
|
|
3
|
+
## What is fast-json-stringify?
|
|
4
|
+
|
|
5
|
+
`fast-json-stringify` is a JSON serialization library developed by the Fastify team. It analyzes JSON schema definitions and compiles them into serialization functions specialized for the exact shape of your payload, so it can skip everything the schema does not describe instead of walking the object generically like `JSON.stringify()` has to.
|
|
6
|
+
|
|
7
|
+
## Introducing express-fast-json-stringify
|
|
8
|
+
|
|
9
|
+
`express-fast-json-stringify` is an npm package that brings the performance benefits of `fast-json-stringify` to Express.js applications. By integrating this package, you can achieve faster JSON serialization, thus improving the overall performance of your application.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
First, install the `express-fast-json-stringify` package:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
npm install express-fast-json-stringify
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
It requires Express 4.16 or newer (Express 5 included) and Node.js 20 or newer.
|
|
20
|
+
|
|
21
|
+
## Creating a JSON Schema
|
|
22
|
+
|
|
23
|
+
Define a schema object that specifies the structure of your JSON responses. This schema will be used by `fast-json-stringify` to optimize the serialization process.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Schema } from 'express-fast-json-stringify';
|
|
27
|
+
|
|
28
|
+
const schema: Schema = {
|
|
29
|
+
title: 'Example Schema',
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
firstName: { type: 'string' },
|
|
33
|
+
lastName: { type: 'string' },
|
|
34
|
+
age: {
|
|
35
|
+
description: 'Age in years',
|
|
36
|
+
type: 'integer',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Applying the Middleware
|
|
43
|
+
|
|
44
|
+
Use the `fastJsonSchema` middleware in your Express routes, passing the schema object as an argument. This will set up the optimized JSON serialization for that route.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import express from 'express';
|
|
48
|
+
import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
49
|
+
|
|
50
|
+
const app = express();
|
|
51
|
+
|
|
52
|
+
const exampleSchema: Schema = {
|
|
53
|
+
title: 'Example Schema',
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
firstName: { type: 'string' },
|
|
57
|
+
lastName: { type: 'string' },
|
|
58
|
+
age: { type: 'integer' },
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
app.get('/', fastJsonSchema(exampleSchema), (req, res, next) => {});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Sending JSON Responses
|
|
66
|
+
|
|
67
|
+
Instead of using the default `res.json()` method, use the `res.fastJson()` method provided by the middleware to send JSON responses. This leverages the speed benefits of fast-json-stringify.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import express from 'express';
|
|
71
|
+
import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
72
|
+
|
|
73
|
+
const app = express();
|
|
74
|
+
|
|
75
|
+
const schema: Schema = {
|
|
76
|
+
title: 'Example Schema',
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
firstName: { type: 'string' },
|
|
80
|
+
lastName: { type: 'string' },
|
|
81
|
+
age: {
|
|
82
|
+
description: 'Age in years',
|
|
83
|
+
type: 'integer',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
app.get('/', fastJsonSchema(schema), (req, res, next) => {
|
|
89
|
+
try {
|
|
90
|
+
const data = {
|
|
91
|
+
firstName: 'Simone',
|
|
92
|
+
lastName: 'Nigro',
|
|
93
|
+
age: 40,
|
|
94
|
+
};
|
|
95
|
+
res.fastJson(data);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
next(error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Response semantics
|
|
103
|
+
|
|
104
|
+
`res.fastJson()` is a drop-in replacement for `res.json()`: only the
|
|
105
|
+
serialization changes, every HTTP detail stays the same.
|
|
106
|
+
|
|
107
|
+
| Behavior | What `res.fastJson()` does |
|
|
108
|
+
| -------------------- | --------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `Content-Type` | `application/json; charset=utf-8`, unless the route already set one with `res.type()`. |
|
|
110
|
+
| `Content-Length` | Always set, from the byte length of the payload, so the response is never chunked. |
|
|
111
|
+
| `ETag` | Follows the app `etag` setting, exactly like `res.send()`. Set `app.set('etag', false)` to skip it. |
|
|
112
|
+
| Conditional requests | A matching `If-None-Match` answers `304` with no body. |
|
|
113
|
+
| `204` and `304` | No body and no `Content-Type`/`Content-Length`/`Transfer-Encoding`. |
|
|
114
|
+
| `HEAD` | Headers only, `Content-Length` included. |
|
|
115
|
+
|
|
116
|
+
The payload is serialized before the status code is inspected, so a body that
|
|
117
|
+
does not match the schema still throws on a `204` — just like `res.json()`.
|
|
118
|
+
|
|
119
|
+
## Taking the schema from your OpenAPI document
|
|
120
|
+
|
|
121
|
+
If you already publish an OpenAPI (or Swagger) document, the response schemas
|
|
122
|
+
are written there — no need to repeat them in the routes. `fastJsonOpenApi`
|
|
123
|
+
takes that document and resolves the schema per route and per status code:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import express from 'express';
|
|
127
|
+
import { fastJsonOpenApi } from 'express-fast-json-stringify';
|
|
128
|
+
|
|
129
|
+
import document from './openapi.json' with { type: 'json' };
|
|
130
|
+
|
|
131
|
+
const app = express();
|
|
132
|
+
|
|
133
|
+
// One middleware for the whole app.
|
|
134
|
+
app.use(fastJsonOpenApi(document));
|
|
135
|
+
|
|
136
|
+
app.get('/users/:id', (req, res, next) => {
|
|
137
|
+
try {
|
|
138
|
+
// Serialized with the schema of `get /users/{id}` -> `200`.
|
|
139
|
+
res.fastJson(user);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
next(error);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
app.post('/users', (req, res, next) => {
|
|
146
|
+
try {
|
|
147
|
+
// ...and this one with the schema of `post /users` -> `201`.
|
|
148
|
+
res.status(201).fastJson(user);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
next(error);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The document is a plain object, so this works with whatever produces yours:
|
|
156
|
+
|
|
157
|
+
| Tool | How to pass it |
|
|
158
|
+
| --------------------------- | ---------------------------------------------------------------------- |
|
|
159
|
+
| `swagger-jsdoc` | `fastJsonOpenApi(swaggerJsdoc(options))` |
|
|
160
|
+
| `swagger-ui-express` | the same object you hand to `swaggerUi.setup(document)` |
|
|
161
|
+
| `express-openapi-validator` | the same document you pass as `apiSpec` (or the parsed YAML/JSON file) |
|
|
162
|
+
| `tsoa` | the generated `swagger.json` |
|
|
163
|
+
| hand written | `import document from './openapi.json' with { type: 'json' }` |
|
|
164
|
+
|
|
165
|
+
There is no dependency on any of them: the middleware only reads `paths`,
|
|
166
|
+
`components.schemas` (OpenAPI 3.x) and `definitions` (Swagger 2.0).
|
|
167
|
+
|
|
168
|
+
### What it resolves
|
|
169
|
+
|
|
170
|
+
- **The operation** comes from the matched Express route, so `/users/:id`
|
|
171
|
+
under a router mounted at `/api` looks up `/api/users/{id}`. Parameter
|
|
172
|
+
modifiers (`:id?`, `:id(\d+)`) are ignored, as OpenAPI has no equivalent.
|
|
173
|
+
A `HEAD` request falls back to the `get` operation, the way Express does.
|
|
174
|
+
- **The response** is matched most specific first: the exact status code, then
|
|
175
|
+
the wildcard range (`2XX`), then `default`. `res.status(404).fastJson(...)`
|
|
176
|
+
therefore serializes with the `404` schema.
|
|
177
|
+
- **`$ref`** is resolved against the document, including recursive references,
|
|
178
|
+
so `#/components/schemas/User` and `#/definitions/User` both just work.
|
|
179
|
+
- **OpenAPI 3.0 and 3.1** are both supported; `nullable: true` is honoured and
|
|
180
|
+
annotation keywords (`example`, `discriminator`, `xml`, ...) are ignored.
|
|
181
|
+
|
|
182
|
+
Routes the document does not describe fall back to `res.json()`, so adding the
|
|
183
|
+
middleware app-wide cannot break an undocumented endpoint. Pass
|
|
184
|
+
`{ strict: true }` to get an error instead of a silent fallback.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// Read a different media type, pin an operation, forward fast-json-stringify options
|
|
188
|
+
app.use(fastJsonOpenApi(document, { contentType: 'application/vnd.api+json' }));
|
|
189
|
+
app.get('/v2/people/:id', fastJsonOpenApi(document, { path: '/users/{id}', method: 'get' }), handler);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Because the schema decides what gets written, a property that is not in the
|
|
193
|
+
document can never reach a client — `npm run example:openapi` shows a
|
|
194
|
+
`passwordHash` being dropped from an otherwise ordinary response object.
|
|
195
|
+
|
|
196
|
+
### Adopting it without touching your routes
|
|
197
|
+
|
|
198
|
+
Everything above needs you to call `res.fastJson()`. In an existing codebase
|
|
199
|
+
that means editing every `res.json()` call site, which is a lot of churn for a
|
|
200
|
+
serialization change. `overrideJson` removes that step:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
app.use(fastJsonOpenApi(document, { overrideJson: true }));
|
|
204
|
+
|
|
205
|
+
// Unchanged route. It is now serialized from the `get /users/{id}` -> `200`
|
|
206
|
+
// schema, with no edit at the call site.
|
|
207
|
+
app.get('/users/:id', (req, res) => res.json(user));
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Only `res.json` is replaced. Express implements `res.send(object)` by calling
|
|
211
|
+
`res.json(object)`, so both entry points are covered by the one hook, while
|
|
212
|
+
`res.send` of a string or a Buffer is left alone.
|
|
213
|
+
|
|
214
|
+
**The override cannot break a route.** It steps aside, and the stock
|
|
215
|
+
`res.json()` runs, whenever:
|
|
216
|
+
|
|
217
|
+
- the document describes no schema for that route and status code;
|
|
218
|
+
- the app sets `json replacer`, `json spaces` or `json escape` — those change
|
|
219
|
+
the bytes `res.json()` writes and a compiled serializer cannot reproduce them;
|
|
220
|
+
- the serializer rejects the body, for instance because a required property is
|
|
221
|
+
missing. Pass `onError` to be told when that happens:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
app.use(
|
|
225
|
+
fastJsonOpenApi(document, {
|
|
226
|
+
overrideJson: true,
|
|
227
|
+
onError: (error, req) => logger.warn({ error, url: req.originalUrl }, 'schema mismatch'),
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`strict` does not apply here: it governs explicit `res.fastJson()` calls, while
|
|
233
|
+
an overridden `res.json()` always falls back rather than throw.
|
|
234
|
+
|
|
235
|
+
`fastJsonSchema` accepts `overrideJson` too, with one difference: a single
|
|
236
|
+
schema describes the _successful_ payload, so only `2xx` responses take the fast
|
|
237
|
+
path. An error body would otherwise be rewritten into the shape of the success
|
|
238
|
+
schema.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
app.get('/users/:id', fastJsonSchema(userSchema, { overrideJson: true }), (req, res) => {
|
|
242
|
+
res.json(user); // serialized through the schema
|
|
243
|
+
res.status(500).json({ error: 'boom' }); // untouched, stock res.json()
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Performance Benefits
|
|
248
|
+
|
|
249
|
+
Using `express-fast-json-stringify` offers several benefits:
|
|
250
|
+
|
|
251
|
+
1. **Increased Speed**: a compiled serializer only writes the properties the schema describes, so the more your objects carry that the response does not need, the more time it saves.
|
|
252
|
+
2. **Reduced CPU Usage**: faster serialization means less CPU time spent on processing, allowing your server to handle more concurrent requests.
|
|
253
|
+
3. **Consistency and Validation**: by defining JSON schemas, you ensure that the serialized data adheres to a predefined structure, improving data consistency and reducing the likelihood of errors — and properties missing from the schema never leak into a response.
|
|
254
|
+
|
|
255
|
+
How much you gain depends on the payload, and on a current V8 it is not a win across the board. Measured on Node.js 26 by `npm run example`:
|
|
256
|
+
|
|
257
|
+
| Case | `fast-json-stringify` vs `JSON.stringify` |
|
|
258
|
+
| ------------------------------------------ | ------------------------------------------- |
|
|
259
|
+
| objects carrying fields outside the schema | much faster (≈9x when 17 of 20 are dropped) |
|
|
260
|
+
| a single small object | comparable |
|
|
261
|
+
| every field serialized | comparable |
|
|
262
|
+
| long strings | slower |
|
|
263
|
+
|
|
264
|
+
Run `npm run example` to get the numbers for your own Node.js version and payload shape before adopting it in a hot path.
|
|
265
|
+
|
|
266
|
+
## Conclusion
|
|
267
|
+
|
|
268
|
+
Integrating `express-fast-json-stringify` into your Express.js application can provide substantial performance improvements when JSON serialization is a bottleneck and your schemas let the serializer skip work. It also guarantees that responses carry exactly the properties the schema describes, which is worth having on its own.
|
|
269
|
+
|
|
270
|
+
To start using `express-fast-json-stringify`, follow the steps outlined in this documentation, and enjoy the benefits of faster JSON serialization in your Express applications. For a runnable demo, see [example/](./example): `npm install && npm run example`.
|
|
271
|
+
|
|
272
|
+
## Support
|
|
273
|
+
|
|
274
|
+
This is an open-source project. Star this [repository](https://github.com/nigrosimone/express-fast-json-stringify), if you like it, or even [donate](https://www.paypal.com/paypalme/snwp). Thank you so much!
|
package/build/main/index.d.ts
CHANGED
package/build/main/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./lib/middleware"), exports);
|
|
18
|
-
|
|
18
|
+
__exportStar(require("./lib/openapi"), exports);
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLG1EQUFpQztBQUNqQyxnREFBOEIifQ==
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import { type Options, type Schema } from 'fast-json-stringify';
|
|
3
|
+
import { type OverrideErrorHandler } from './override';
|
|
3
4
|
export type { Schema, Options } from 'fast-json-stringify';
|
|
5
|
+
export type FastJsonSchemaOptions = Omit<Options, 'mode'> & {
|
|
6
|
+
/**
|
|
7
|
+
* Also route `res.json()` — and therefore `res.send(object)`, which Express
|
|
8
|
+
* implements on top of it — through the compiled serializer.
|
|
9
|
+
*
|
|
10
|
+
* Off by default. Because a single schema describes the successful payload,
|
|
11
|
+
* only `2xx` responses take the fast path: an error body would otherwise be
|
|
12
|
+
* rewritten into the shape of the success schema.
|
|
13
|
+
*/
|
|
14
|
+
readonly overrideJson?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Called when an overridden `res.json()` could not use the fast path because
|
|
17
|
+
* the serializer threw. The response falls back to the stock `res.json()`
|
|
18
|
+
* either way; this is only so the mismatch is visible.
|
|
19
|
+
*/
|
|
20
|
+
readonly onError?: OverrideErrorHandler;
|
|
21
|
+
};
|
|
4
22
|
/**
|
|
5
|
-
*
|
|
23
|
+
* Build a stringify function using a schema of the documents that should be stringified
|
|
6
24
|
* @param {Schema} schema The schema used to stringify values
|
|
7
25
|
* @param {Options} options The options to use (optional)
|
|
8
26
|
* @see https://www.npmjs.com/package/fast-json-stringify
|
|
@@ -10,7 +28,7 @@ export type { Schema, Options } from 'fast-json-stringify';
|
|
|
10
28
|
* Examples:
|
|
11
29
|
* ```ts
|
|
12
30
|
* import express from 'express';
|
|
13
|
-
* import {
|
|
31
|
+
* import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
14
32
|
*
|
|
15
33
|
* const app = express();
|
|
16
34
|
*
|
|
@@ -44,7 +62,7 @@ export type { Schema, Options } from 'fast-json-stringify';
|
|
|
44
62
|
* });
|
|
45
63
|
* ```
|
|
46
64
|
*/
|
|
47
|
-
export declare const fastJsonSchema: (schema: Schema, options?:
|
|
65
|
+
export declare const fastJsonSchema: (schema: Schema, options?: FastJsonSchemaOptions) => (req: Request, res: Response, next: NextFunction) => void;
|
|
48
66
|
declare global {
|
|
49
67
|
namespace Express {
|
|
50
68
|
interface Response {
|
|
@@ -53,11 +71,11 @@ declare global {
|
|
|
53
71
|
*
|
|
54
72
|
* Examples:
|
|
55
73
|
* ```ts
|
|
56
|
-
* res.fastJson({ user: '
|
|
57
|
-
* res.status(200).fastJson({ user: '
|
|
74
|
+
* res.fastJson({ user: 'Simone Nigro' });
|
|
75
|
+
* res.status(200).fastJson({ user: 'Simone Nigro' });
|
|
58
76
|
* ```
|
|
59
77
|
*/
|
|
60
|
-
fastJson: (
|
|
78
|
+
fastJson: (body: any) => Response;
|
|
61
79
|
}
|
|
62
80
|
}
|
|
63
81
|
}
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
2
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
15
|
};
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
17
|
exports.fastJsonSchema = void 0;
|
|
7
18
|
const fast_json_stringify_1 = __importDefault(require("fast-json-stringify"));
|
|
19
|
+
const override_1 = require("./override");
|
|
20
|
+
const send_1 = require("./send");
|
|
8
21
|
/**
|
|
9
|
-
*
|
|
22
|
+
* Build a stringify function using a schema of the documents that should be stringified
|
|
10
23
|
* @param {Schema} schema The schema used to stringify values
|
|
11
24
|
* @param {Options} options The options to use (optional)
|
|
12
25
|
* @see https://www.npmjs.com/package/fast-json-stringify
|
|
@@ -14,7 +27,7 @@ const fast_json_stringify_1 = __importDefault(require("fast-json-stringify"));
|
|
|
14
27
|
* Examples:
|
|
15
28
|
* ```ts
|
|
16
29
|
* import express from 'express';
|
|
17
|
-
* import {
|
|
30
|
+
* import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
18
31
|
*
|
|
19
32
|
* const app = express();
|
|
20
33
|
*
|
|
@@ -49,28 +62,30 @@ const fast_json_stringify_1 = __importDefault(require("fast-json-stringify"));
|
|
|
49
62
|
* ```
|
|
50
63
|
*/
|
|
51
64
|
const fastJsonSchema = (schema, options) => {
|
|
52
|
-
if (!schema) {
|
|
65
|
+
if (!schema || (typeof schema !== 'object' && typeof schema !== 'boolean')) {
|
|
53
66
|
throw new TypeError(`express-fast-json-stringify: invalid schema`);
|
|
54
67
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
68
|
+
const _a = options !== null && options !== void 0 ? options : {}, { overrideJson = false, onError } = _a, fastJsonOptions = __rest(_a, ["overrideJson", "onError"]);
|
|
69
|
+
const fjs = (0, fast_json_stringify_1.default)(schema, fastJsonOptions);
|
|
70
|
+
return (req, res, next) => {
|
|
57
71
|
/**
|
|
58
72
|
* Send JSON response.
|
|
59
73
|
*
|
|
60
74
|
* Examples:
|
|
61
75
|
* ```ts
|
|
62
|
-
* res.fastJson({ user: '
|
|
63
|
-
* res.status(200).fastJson({ user: '
|
|
76
|
+
* res.fastJson({ user: 'Simone Nigro' });
|
|
77
|
+
* res.status(200).fastJson({ user: 'Simone Nigro' });
|
|
64
78
|
* ```
|
|
65
79
|
*/
|
|
66
|
-
res.fastJson = (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
res.fastJson = (body) => (0, send_1.sendJson)(req, res, fjs(body));
|
|
81
|
+
if (overrideJson) {
|
|
82
|
+
// A single schema describes the successful payload, so applying it to an
|
|
83
|
+
// error body would rewrite it into the wrong shape. Only 2xx responses
|
|
84
|
+
// take the fast path; everything else keeps the stock res.json().
|
|
85
|
+
(0, override_1.overrideResJson)(req, res, (status) => (status >= 200 && status < 300 ? fjs : null), onError);
|
|
86
|
+
}
|
|
72
87
|
next();
|
|
73
88
|
};
|
|
74
89
|
};
|
|
75
90
|
exports.fastJsonSchema = fastJsonSchema;
|
|
76
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
91
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvbWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7OztBQUNBLDhFQUEwRTtBQUUxRSx5Q0FBd0U7QUFDeEUsaUNBQWtDO0FBc0JsQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMENHO0FBQ0ksTUFBTSxjQUFjLEdBQUcsQ0FBQyxNQUFjLEVBQUUsT0FBK0IsRUFBRSxFQUFFO0lBQ2hGLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksT0FBTyxNQUFNLEtBQUssU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUMzRSxNQUFNLElBQUksU0FBUyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUNELE1BQU0sS0FBd0QsT0FBTyxhQUFQLE9BQU8sY0FBUCxPQUFPLEdBQUksRUFBRSxFQUFyRSxFQUFFLFlBQVksR0FBRyxLQUFLLEVBQUUsT0FBTyxPQUFzQyxFQUFqQyxlQUFlLGNBQW5ELDJCQUFxRCxDQUFnQixDQUFDO0lBQzVFLE1BQU0sR0FBRyxHQUFHLElBQUEsNkJBQVEsRUFBQyxNQUFNLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDOUMsT0FBTyxDQUFDLEdBQVksRUFBRSxHQUFhLEVBQUUsSUFBa0IsRUFBRSxFQUFFO1FBQ3pEOzs7Ozs7OztXQVFHO1FBQ0gsR0FBRyxDQUFDLFFBQVEsR0FBRyxDQUFDLElBQVMsRUFBWSxFQUFFLENBQUMsSUFBQSxlQUFRLEVBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUV0RSxJQUFJLFlBQVksRUFBRSxDQUFDO1lBQ2pCLHlFQUF5RTtZQUN6RSx1RUFBdUU7WUFDdkUsa0VBQWtFO1lBQ2xFLElBQUEsMEJBQWUsRUFBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLE1BQU0sSUFBSSxHQUFHLElBQUksTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMvRixDQUFDO1FBRUQsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDLENBQUM7QUFDSixDQUFDLENBQUM7QUEzQlcsUUFBQSxjQUFjLGtCQTJCekIifQ==
|