Skip to content

Commit 032756e

Browse files
authored
feat: standalone mode (#18)
1 parent 09da8b0 commit 032756e

File tree

11 files changed

+416
-14
lines changed

11 files changed

+416
-14
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ jobs:
1515
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
1616
with:
1717
license-check: true
18+
lint: true

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,98 @@ You can also override the default configuration by passing the [`serializerOpts`
2626
This module is already used as default by Fastify.
2727
If you need to provide to your server instance a different version, refer to [the official doc](https://www.fastify.io/docs/latest/Reference/Server/#schemacontroller).
2828

29+
### fast-json-stringify Standalone
30+
31+
`[email protected]` introduces the [standalone feature](https://github.com/fastify/fast-json-stringify#standalone) that let you to pre-compile your schemas and use them in your application for a faster startup.
32+
33+
To use this feature, you must be aware of the following:
34+
35+
1. You must generate and save the application's compiled schemas.
36+
2. Read the compiled schemas from the file and provide them back to your Fastify application.
37+
38+
39+
#### Generate and save the compiled schemas
40+
41+
Fastify helps you to generate the serialization schemas functions and it is your choice to save them where you want.
42+
To accomplish this, you must use a new compiler: `@fastify/fast-json-stringify-compiler/standalone`.
43+
44+
You must provide 2 parameters to this compiler:
45+
46+
- `readMode: false`: a boolean to indicate that you want generate the schemas functions string.
47+
- `storeFunction`" a sync function that must store the source code of the schemas functions. You may provide an async function too, but you must manage errors.
48+
49+
When `readMode: false`, **the compiler is meant to be used in development ONLY**.
50+
51+
52+
```js
53+
const { StandaloneSerializer } = require('@fastify/fast-json-stringify-compiler')
54+
55+
const factory = StandaloneSerializer({
56+
readMode: false,
57+
storeFunction (routeOpts, schemaSerializationCode) {
58+
// routeOpts is like: { schema, method, url, httpStatus }
59+
// schemaSerializationCode is a string source code that is the compiled schema function
60+
const fileName = generateFileName(routeOpts)
61+
fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode)
62+
}
63+
})
64+
65+
const app = fastify({
66+
jsonShorthand: false,
67+
schemaController: {
68+
compilersFactory: {
69+
buildSerializer: factory
70+
}
71+
}
72+
})
73+
74+
// ... add all your routes with schemas ...
75+
76+
app.ready().then(() => {
77+
// at this stage all your schemas are compiled and stored in the file system
78+
// now it is important to turn off the readMode
79+
})
80+
```
81+
82+
#### Read the compiled schemas functions
83+
84+
At this stage, you should have a file for every route's schema.
85+
To use them, you must use the `@fastify/fast-json-stringify-compiler/standalone` with the parameters:
86+
87+
- `readMode: true`: a boolean to indicate that you want read and use the schemas functions string.
88+
- `restoreFunction`" a sync function that must return a function to serialize the route's payload.
89+
90+
Important keep away before you continue reading the documentation:
91+
92+
- when you use the `readMode: true`, the application schemas are not compiled (they are ignored). So, if you change your schemas, you must recompile them!
93+
- as you can see, you must relate the route's schema to the file name using the `routeOpts` object. You may use the `routeOpts.schema.$id` field to do so, it is up to you to define a unique schema identifier.
94+
95+
```js
96+
const { StandaloneSerializer } = require('@fastify/fast-json-stringify-compiler')
97+
98+
const factory = StandaloneSerializer({
99+
readMode: true,
100+
restoreFunction (routeOpts) {
101+
// routeOpts is like: { schema, method, url, httpStatus }
102+
const fileName = generateFileName(routeOpts)
103+
return require(path.join(__dirname, fileName))
104+
}
105+
})
106+
107+
const app = fastify({
108+
jsonShorthand: false,
109+
schemaController: {
110+
compilersFactory: {
111+
buildSerializer: factory
112+
}
113+
}
114+
})
115+
116+
// ... add all your routes with schemas as before...
117+
118+
app.listen({ port: 3000 })
119+
```
120+
29121
### How it works
30122

31123
This module provide a factory function to produce [Serializer Compilers](https://www.fastify.io/docs/latest/Reference/Server/#serializercompiler) functions.

index.d.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
import { Options as FJSOptions } from 'fast-json-stringify'
22

3+
export type { Options } from 'fast-json-stringify'
4+
35
export type SerializerCompiler = (
46
externalSchemas: unknown,
57
options: FJSOptions
68
) => (doc: any) => string;
79

8-
export declare function SerializerSelector(): SerializerCompiler;
10+
export type RouteDefinition = {
11+
method: string,
12+
url: string,
13+
httpStatus: string,
14+
schema?: unknown,
15+
}
916

10-
export type { Options } from 'fast-json-stringify'
17+
export interface StandaloneOptions {
18+
readMode: Boolean,
19+
storeFunction?(opts: RouteDefinition, schemaSerializationCode: string): void,
20+
restoreFunction?(opts: RouteDefinition): void,
21+
}
22+
23+
declare function SerializerSelector(): SerializerCompiler;
24+
declare function StandaloneSerializer(options: StandaloneOptions): SerializerCompiler;
1125

12-
export default SerializerSelector;
26+
export default SerializerSelector;
27+
export {
28+
SerializerSelector,
29+
StandaloneSerializer,
30+
};

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ function responseSchemaCompiler (fjsOpts, { schema /* method, url, httpStatus */
1818
}
1919

2020
module.exports = SerializerSelector
21+
module.exports.default = SerializerSelector
22+
module.exports.SerializerSelector = SerializerSelector
23+
module.exports.StandaloneSerializer = require('./standalone')

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
"main": "index.js",
66
"types": "index.d.ts",
77
"scripts": {
8+
"lint": "standard",
89
"lint:fix": "standard --fix",
9-
"unit": "tap --100 test/**/*.test.js",
10-
"test": "standard && npm run unit && npm run test:typescript",
10+
"unit": "tap test/**/*.test.js",
11+
"test": "npm run unit && npm run test:typescript",
12+
"posttest": "rimraf test/fjs-generated*.js",
1113
"test:typescript": "tsd"
1214
},
1315
"repository": {
@@ -23,6 +25,8 @@
2325
"homepage": "https://github.com/fastify/fast-json-stringify-compiler#readme",
2426
"devDependencies": {
2527
"fastify": "^4.0.0",
28+
"rimraf": "^3.0.2",
29+
"sanitize-filename": "^1.6.3",
2630
"standard": "^17.0.0",
2731
"tap": "^16.0.0",
2832
"tsd": "^0.22.0"

standalone.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict'
2+
3+
const SerializerSelector = require('./index')
4+
5+
function StandaloneSerializer (options = { readMode: true }) {
6+
if (options.readMode === true && typeof options.restoreFunction !== 'function') {
7+
throw new Error('You must provide a function for the restoreFunction-option when readMode ON')
8+
}
9+
10+
if (options.readMode !== true && typeof options.storeFunction !== 'function') {
11+
throw new Error('You must provide a function for the storeFunction-option when readMode OFF')
12+
}
13+
14+
if (options.readMode === true) {
15+
// READ MODE: it behalf only in the restore function provided by the user
16+
return function wrapper () {
17+
return function (opts) {
18+
return options.restoreFunction(opts)
19+
}
20+
}
21+
}
22+
23+
// WRITE MODE: it behalf on the default SerializerSelector, wrapping the API to run the Ajv Standalone code generation
24+
const factory = SerializerSelector()
25+
return function wrapper (externalSchemas, serializerOpts = {}) {
26+
// to generate the serialization source code, this option is mandatory
27+
serializerOpts.mode = 'standalone'
28+
29+
const compiler = factory(externalSchemas, serializerOpts)
30+
return function (opts) { // { schema/*, method, url, httpPart */ }
31+
const serializeFuncCode = compiler(opts)
32+
33+
options.storeFunction(opts, serializeFuncCode)
34+
35+
// eslint-disable-next-line no-new-func
36+
return new Function(serializeFuncCode)
37+
}
38+
}
39+
}
40+
41+
module.exports = StandaloneSerializer
42+
module.exports.default = StandaloneSerializer

test/plugin.test.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ const externalSchemas2 = Object.freeze({
2525
}
2626
})
2727

28-
const fastifyFjsOptionsDefault = Object.freeze({
29-
customOptions: {}
30-
})
28+
const fastifyFjsOptionsDefault = Object.freeze({})
3129

3230
t.test('basic usage', t => {
3331
t.plan(1)

0 commit comments

Comments
 (0)