fastify-ata 0.4.0 → 0.4.2
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/README.md +40 -0
- package/index.js +4 -1
- package/package.json +2 -2
- package/test.js +25 -0
package/README.md
CHANGED
|
@@ -44,6 +44,37 @@ fastify.listen({ port: 3000 })
|
|
|
44
44
|
|
|
45
45
|
All your existing JSON Schema route definitions work as-is.
|
|
46
46
|
|
|
47
|
+
## TypeScript
|
|
48
|
+
|
|
49
|
+
Write plain JSON Schema and get typed route handlers, no builder DSL. Add the `AtaTypeProvider` and author schemas with `defineSchema`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import Fastify from 'fastify'
|
|
53
|
+
import fastifyAta from 'fastify-ata'
|
|
54
|
+
import { defineSchema } from 'ata-validator'
|
|
55
|
+
|
|
56
|
+
const app = Fastify().withTypeProvider<fastifyAta.AtaTypeProvider>()
|
|
57
|
+
await app.register(fastifyAta)
|
|
58
|
+
|
|
59
|
+
app.post('/user', {
|
|
60
|
+
schema: {
|
|
61
|
+
body: defineSchema({
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: { name: { type: 'string' }, age: { type: 'integer' } },
|
|
64
|
+
required: ['name'],
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
}, (req, reply) => {
|
|
68
|
+
req.body.name // string
|
|
69
|
+
req.body.age // number | undefined
|
|
70
|
+
reply.send({ ok: true })
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`defineSchema` preserves the schema's literal types, so `request.body`, `request.query`, `request.params`, and `request.headers` are inferred from the schema. Same idea as `@fastify/type-provider-typebox`, from plain JSON Schema.
|
|
75
|
+
|
|
76
|
+
`ata-validator` falls back to a pure-JS engine where the native addon is not available (Cloudflare Workers, browsers, Bun), so fastify-ata runs in those environments too.
|
|
77
|
+
|
|
47
78
|
## Options
|
|
48
79
|
|
|
49
80
|
```js
|
|
@@ -51,9 +82,18 @@ fastify.register(fastifyAta, {
|
|
|
51
82
|
coerceTypes: true, // convert "42" -> 42 for integer fields
|
|
52
83
|
removeAdditional: true, // strip properties not in schema
|
|
53
84
|
abortEarly: true, // skip detailed error collection (faster invalid path)
|
|
85
|
+
prettyErrors: true, // 400 message carries the ATA code + a did-you-mean
|
|
54
86
|
})
|
|
55
87
|
```
|
|
56
88
|
|
|
89
|
+
With `prettyErrors`, a failed request returns a compiler-style message instead of the plain ajv text:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
body must have required property 'name' [ATA7001] (did you mean `name` instead of `nme`?)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Off by default to keep the ajv-compatible message shape.
|
|
96
|
+
|
|
57
97
|
`abortEarly` replaces the error list with a shared stub. Good for public endpoints where only the accept/reject decision reaches the caller. On a 10-property schema the invalid path drops from roughly 15 ns/op to 3.7 ns/op.
|
|
58
98
|
|
|
59
99
|
## Standalone Mode (Pre-compiled)
|
package/index.js
CHANGED
|
@@ -31,7 +31,10 @@ function fastifyAta(fastify, opts, done) {
|
|
|
31
31
|
fastify.setValidatorCompiler(({ schema }) => {
|
|
32
32
|
let validator = cache.get(schema)
|
|
33
33
|
if (!validator) {
|
|
34
|
-
|
|
34
|
+
// Pass schemas registered via `fastify.addSchema` so cross-schema `$ref`
|
|
35
|
+
// (e.g. `{ $ref: 'shared#' }`) resolves. Compilers run at ready() time,
|
|
36
|
+
// after every addSchema, so getSchemas() returns the full bucket.
|
|
37
|
+
validator = new Validator(schema, { ...validatorOpts, schemas: fastify.getSchemas() })
|
|
35
38
|
cache.set(schema, validator)
|
|
36
39
|
}
|
|
37
40
|
const validate = (data) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify-ata",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Fastify plugin for ata-validator. Runtime-competitive with the default, 24x faster serverless cold start, Standard Schema V1.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/ata-core/fastify-ata#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"ata-validator": "^0.17.
|
|
29
|
+
"ata-validator": "^0.17.5",
|
|
30
30
|
"fastify-plugin": "^5.1.0",
|
|
31
31
|
"sanitize-filename": "^1.6.4"
|
|
32
32
|
},
|
package/test.js
CHANGED
|
@@ -459,6 +459,31 @@ async function run() {
|
|
|
459
459
|
await tApp.close()
|
|
460
460
|
*/
|
|
461
461
|
|
|
462
|
+
// Cross-schema $ref via addSchema: the plugin must pass external schemas to ata,
|
|
463
|
+
// and ata must resolve both external $id refs and draft-07 #anchor refs.
|
|
464
|
+
const refApp = fastify()
|
|
465
|
+
await refApp.register(fastifyAta)
|
|
466
|
+
refApp.addSchema({ $id: 'http://foo/test', type: 'object', properties: { id: { type: 'number' } } })
|
|
467
|
+
refApp.post('/ref', {
|
|
468
|
+
schema: {
|
|
469
|
+
body: {
|
|
470
|
+
$id: 'http://foo/user',
|
|
471
|
+
type: 'object',
|
|
472
|
+
definitions: { address: { $id: '#address', type: 'object', properties: { city: { type: 'string' } }, required: ['city'] } },
|
|
473
|
+
required: ['address'],
|
|
474
|
+
properties: { test: { $ref: 'http://foo/test#' }, address: { $ref: '#address' } },
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
}, (req, reply) => reply.send({ ok: true }))
|
|
478
|
+
await refApp.ready()
|
|
479
|
+
const refGood = await refApp.inject({ method: 'POST', url: '/ref', payload: { test: { id: 1 }, address: { city: 'x' } } })
|
|
480
|
+
const refBadExt = await refApp.inject({ method: 'POST', url: '/ref', payload: { test: { id: 'no' }, address: { city: 'x' } } })
|
|
481
|
+
const refBadLocal = await refApp.inject({ method: 'POST', url: '/ref', payload: { test: { id: 1 }, address: {} } })
|
|
482
|
+
assert(refGood.statusCode === 200, 'cross-schema $ref: valid request accepted')
|
|
483
|
+
assert(refBadExt.statusCode === 400 && !/cannot resolve/.test(refBadExt.payload), 'cross-schema $ref: external $id ref enforced')
|
|
484
|
+
assert(refBadLocal.statusCode === 400 && !/cannot resolve/.test(refBadLocal.payload), 'cross-schema $ref: local #anchor enforced')
|
|
485
|
+
await refApp.close()
|
|
486
|
+
|
|
462
487
|
console.log(`\n${pass}/${pass + fail} tests passed\n`)
|
|
463
488
|
process.exit(fail > 0 ? 1 : 0)
|
|
464
489
|
}
|