ArkType-powered request validation and documentation plugin for Kaapi.
npm install @kaapi/validator-arktypeArkType-powered validation plugin for Kaapi. Validate request params, payload, query, headers, and state using ArkType schemas. Includes built-in documentation helpers for seamless API docs generation.
> β οΈ This library is ESMβonly. It requires an environment that supports ESM imports.
---
``bash`
npm install @kaapi/validator-arktype
Requires ArkType:
`bash`
npm install arktype@^2.1.25
---
`ts
import { Kaapi } from '@kaapi/kaapi';
import { validatorArk } from '@kaapi/validator-arktype';
import { type } from 'arktype';
const app = new Kaapi({
port: 3000,
host: 'localhost',
docs: {
disabled: false, // explicitly enables documentation generation
},
});
await app.extend(validatorArk); // register the plugin
`
---
`ts
import { ValidatorArkSchema } from '@kaapi/validator-arktype';
import { type } from 'arktype';
const routeSchema: ValidatorArkSchema = {
payload: type({
name: 'string',
}),
};
`
---
`ts
app.base()
.ark(routeSchema)
.route(
{
method: 'POST',
path: '/items',
},
(req) => ({ id: Date.now(), name: req.payload.name })
);
// or using inline handler
/*
app.base().ark(routeSchema).route({
method: 'POST',
path: '/items',
handler: req => ({ id: Date.now(), name: req.payload.name })
})
*/
`
---
You can use withSchema to create validated routes without directly chaining from app.base().
This cleanly separates route construction from app registration.
`ts
import { withSchema } from '@kaapi/validator-arktype'
import { type } from 'arktype';
const schema = {
payload: type({
name: 'string'
})
}
const route = withSchema(schema).route({
method: 'POST',
path: '/items',
handler: req => ({ id: Date.now(), name: req.payload.name })
})
// later, during app setup
app.route(route)
`
This is the most flexible and convenient way to use @kaapi/validator-arktype when building modular APIs.
---
Control how validation failures are handled:
| Value | Behavior | Safe? | Description |
| ---------- | ---------------------------- | ------------------------- | ------------------------------- |
| 'error' | Reject with validation error | β
| Default safe behavior |'log'
| | Log and reject | β
| Useful for observability |function
| | Custom handler | β
(developer-controlled) | Must return or throw explicitly |'ignore'
| | β Not supported | β | Unsafe and not implemented |
---
You can override ArkType validation behavior globally for all routes, or per route as needed.
#### π Global Override (All Routes)
`ts
const app = new Kaapi({
// ...
routes: {
plugins: {
ark: {
failAction: 'log',
},
},
},
});
await app.extend(validatorArk);
`
This logs validation errors before throwing them for all ArkType-validated routes.
#### π Per-Route Override
`tsHello ${name}!
app.base()
.ark({
query: type({
name: type(['string', '@', 'Optional name to personalize the greeting response'])
.pipe((v) => v?.trim() ?? '')
.to('0 < string <= 10')
.default('World')
}),
failAction: async (request, h, err) => {
if (Boom.isBoom(err)) {
return h
.response({
...err.output.payload,
details: err.data?.validationError?.issues,
})
.code(err.output.statusCode)
.takeover();
}
return err;
},
})
.route({
path: '/greetings',
method: 'GET',
handler: ({ query: { name } }) => ,`
});
---
Multipart file uploads with ArkType validation is supported. Here's how to validate an uploaded image file and stream it back in the response:
`ts`
app.base()
.ark({
payload: type({
file: type({
_data: type.instanceOf(Buffer),
hapi: type({
filename: 'string',
headers: {
'content-type': '\'image/jpeg\' | \'image/jpg\' | \'image/png\'',
},
}),
}),
}),
})
.route(
{
method: 'POST',
path: '/upload-image',
options: {
description: 'Upload an image',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data',
multipart: { output: 'stream' },
maxBytes: 1024 * 3_000,
},
},
},
(req, h) => h.response(req.payload.file._data).type(req.payload.file.hapi.headers['content-type'])
);
- type({ ... }) is used to accommodate the structure of multipart file metadata._data: type.instanceOf(Buffer)
- The field is automatically interpreted as a binary field by the documentation generator.
- This ensures correct OpenAPI and Postman documentation is generated, with the file field shown as a binary upload.
- The route streams the uploaded image back with its original content type.
---
Prefer Joi or migrating gradually? No problem.
You can still use app.route(...) with Joi-based validation while adopting ArkType via app.base().ark(...).route(...)`. This dual-mode support ensures graceful evolution, allowing traditional and modern routes to coexist without breaking changes.
---
MIT
> This package is tested as part of the Kaapi monorepo. See the main Kaapi README for coverage details.
Contributions, issues, and feature requests are welcome! Feel free to open a discussion or submit a pull request.