ata fits into Express as a small middleware factory. Validate the parsed body before the route handler sees it.
npm install ata-validator expressconst express = require('express')
const { Validator } = require('ata-validator')
function ataBody(schema, opts = {}) {
const v = new Validator(schema, opts)
return (req, res, next) => {
const r = v.validate(req.body)
if (!r.valid) {
return res.status(400).json({ error: 'validation failed', errors: r.errors })
}
next()
}
}
const app = express()
app.use(express.json())
const userSchema = {
type: 'object',
properties: {
id: { type: 'integer', minimum: 1 },
name: { type: 'string', minLength: 1 },
email: { type: 'string' },
},
required: ['id', 'name', 'email'],
}
app.post('/users', ataBody(userSchema), (req, res) => {
res.json({ ok: true, id: req.body.id })
})
app.listen(3000)For high-throughput route guards that only need reject / accept:
app.post('/ingest', ataBody(userSchema, { abortEarly: true }), handler)On failure the client receives { errors: [{ message: 'validation failed' }] } without the detailed paths, but the invalid path runs ~4x faster per request.
If the route has bodyParser.raw({ type: 'application/json' }) the body arrives as a Buffer. Use simdjson validation and skip JSON.parse for rejected payloads:
const { Validator } = require('ata-validator')
const v = new Validator(userSchema)
app.post(
'/ingest',
express.raw({ type: 'application/json', limit: '10mb' }),
(req, res) => {
if (!v.isValid(req.body)) {
return res.status(400).json({ error: 'invalid' })
}
const parsed = JSON.parse(req.body.toString())
// ...
},
)Useful for webhook endpoints where most traffic is untrusted.
Same pattern, pointing at a different slot on the request:
function ataQuery(schema) {
const v = new Validator(schema)
return (req, res, next) => {
const r = v.validate(req.query)
if (!r.valid) return res.status(400).json({ errors: r.errors })
next()
}
}
app.get('/search', ataQuery({
type: 'object',
properties: { q: { type: 'string', minLength: 1 } },
required: ['q'],
}), handler)Default errors have ajv's shape (keyword, instancePath, params, message). For end-user responses, map to a flatter structure:
function ataBody(schema, opts = {}) {
const v = new Validator(schema, opts)
return (req, res, next) => {
const r = v.validate(req.body)
if (!r.valid) {
const issues = r.errors.map((e) => ({
field: e.instancePath.replace(/^\//, ''),
message: e.message,
}))
return res.status(400).json({ error: 'validation failed', issues })
}
next()
}
}- Works identically with Connect-style frameworks (Restify, server applications using Express-style middleware).
- Hold the Validator instance at module scope to reuse the compiled validator across requests.
- For schemas that change per request (very rare), instantiate inside the middleware; the lazy-compile path keeps construction under a microsecond.