Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ignore-scripts=true
min-release-age=7
; temporary: fast-json-stringify@7.3.0 was published on 2026-11-05, remove after 2026-11-12
min-release-age-exclude[]=fast-json-stringify
package-lock=false
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ The `fast-json-stringify` configuration is the default one. You can check the de

You can also override the default configuration by passing the [`serializerOpts`](https://fastify.dev/docs/latest/Reference/Server/#serializeropts) configuration to the Fastify instance.

#### `objectMechanism: 'native'`

`fast-json-stringify@7.3.0` added the [`objectMechanism`](https://github.com/fastify/fast-json-stringify/#object-mechanism) option.
With `'native'`, the serializer projects the payload into schema-shaped objects and delegates to `JSON.stringify`, so on Node.js 25+ V8 stays on its fast path while the response schema is still applied (undeclared properties stripped, coercions and formats as usual). The output is identical to the default mechanism, and the option is a no-op on Node.js < 25.

```js
const app = fastify({
serializerOpts: {
objectMechanism: 'native'
}
})
```

## Usage

This module is already used as default by Fastify.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@
"tstyche": "^7.1.0"
},
"dependencies": {
"fast-json-stringify": "^7.0.0"
"fast-json-stringify": "^7.3.0"
}
}
62 changes: 62 additions & 0 deletions test/object-mechanism.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict'

const { test } = require('node:test')
const fastify = require('fastify')
const FjsCompiler = require('../index')

const userSchema = Object.freeze({
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
createdAt: { type: 'string', format: 'date-time' }
},
required: ['id', 'name']
})

test('objectMechanism is forwarded to fast-json-stringify', t => {
t.plan(1)
const factory = FjsCompiler()
t.assert.throws(() => {
factory({}, { objectMechanism: 'not-a-mechanism' })({ schema: userSchema })
}, /Unsupported object mechanism not-a-mechanism/)
})

test('objectMechanism: native produces the same output as the default mechanism', t => {
t.plan(1)
const factory = FjsCompiler()
const serializeDefault = factory({}, {})({ schema: userSchema })
const serializeNative = factory({}, { objectMechanism: 'native' })({ schema: userSchema })

const input = { id: 1, name: 'alice', passwordHash: 'secret', createdAt: new Date(0) }
t.assert.equal(serializeNative(input), serializeDefault(input))
})

test('fastify integration with objectMechanism: native', async t => {
const factory = FjsCompiler()

const app = fastify({
serializerOpts: {
objectMechanism: 'native'
},
schemaController: {
compilersFactory: {
buildSerializer: factory
}
}
})

app.get('/', {
handler: async () => ({ id: 1, name: 'alice', passwordHash: 'secret', createdAt: new Date(0) }),
schema: {
response: {
200: userSchema
}
}
})

const res = await app.inject({ url: '/', method: 'GET' })

t.assert.equal(res.statusCode, 200)
t.assert.equal(res.body, '{"id":1,"name":"alice","createdAt":"1970-01-01T00:00:00.000Z"}')
})
4 changes: 4 additions & 0 deletions types/index.tst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ import SerializerSelector, {
expect(serializeFunc).type.toBe<Serializer>()

expect(serializeFunc({ name: 'hello' })).type.toBe<string>()

expect(factory({}, { objectMechanism: 'native' })).type.toBe<SerializerCompiler>()
expect(factory({}, { objectMechanism: 'default' })).type.toBe<SerializerCompiler>()
expect(factory).type.not.toBeCallableWith({}, { objectMechanism: 'not-a-mechanism' })
}

/**
Expand Down
Loading