Open ayZagen opened 1 month ago
It appears that recent TypeBox updates have significantly narrowed the performance gap.
$ bun add @sinclair/typebox@0.32.4 # the version Elysia uses
$ bun run bench.mts
cpu: unknown
runtime: bun 1.1.24 (arm64-linux)
benchmark time (avg) (min … max)
-------------------------------------------------
• json-schema
-------------------------------------------------
typebox 1'291 ns/iter (1'172 ns … 2'877 ns)
ajv 151 ns/iter (124 ns … 709 ns)
summary for json-schema
ajv
8.55x faster than typebox
$ bun add @sinclair/typebox@0.33.5
$ bun run bench.mts
cpu: unknown
runtime: bun 1.1.24 (arm64-linux)
benchmark time (avg) (min … max)
-------------------------------------------------
• json-schema
-------------------------------------------------
typebox 298 ns/iter (247 ns … 1'799 ns)
ajv 146 ns/iter (119 ns … 696 ns)
summary for json-schema
ajv
2.04x faster than typebox
@lilprs I dont want this thread to be turn into a benchmarking thread as ajv offers more than typebox. I mentioned those points in the post.
As for benchmark result, it is great to hear typebox improving but the benchmark script I posted above was retrieving only First error for typebox (TypeboxValidator.Errors(val).First()
)
I have updated the script and the result with latest versions.
May we have more data validation library for elysia? I'd like to work with ajv too, it's simple and performance.
I second this, well... not ajv, but I'd love to use EffectTS/schema as my whole stack is built around EffectTS. :) I think Elysia would highly benefit from this integration of other parts of the ecosystem. :)
Currently typebox is used for schema validation and I can guess it is chosen for great performance and awesome TypeScript support.
However, In real world applications, following points are mostly necessary with JSON schemas:
Precise validation errors Typebox really lacks in this area. For example,
Union
schema errors.Conforming defaults (
default
parameter in json schema) In Typebox we must useDefault
functionRemoving additional properties (
additionalProperties: false
) In Typebox we must useClean
functionTo conform all above points with typebox you must do following:
In ajv you can pass them as options.
Now when you run benchmarks you will see ajv is more performant than typebox.
We can still use
TypeBox
as schema definitions and pass them toajv
internally. End-Users will not notice that much difference.ajv
is not a new guy in the town and it is used internally byfastify
.Here is a simple benchmark script:
bench.ts
```ts import { Type } from "@sinclair/typebox" import { TypeCompiler } from "@sinclair/typebox/compiler" import { Clean, Default } from "@sinclair/typebox/value" import Ajv from "ajv" import ajvErrors from "ajv-errors" import { bench, group, run } from "mitata" const RawSchema = Type.Object({ type: Type.Literal("email"), name: Type.String(), details: Type.Object( { from: Type.String({ default: "mydefault", maxLength: 512, }), subject: Type.String({ maxLength: 512, }), }, { default: {} }, ), }) const ajv = ajvErrors( new Ajv({ strict: true, strictRequired: false, removeAdditional: true, useDefaults: true, allErrors: true, allowUnionTypes: true, $data: true, discriminator: true }), ) const TypeboxValidator = TypeCompiler.Compile(RawSchema) const AjvValidator = ajv.compile(RawSchema) group("json-schema", () => { bench("typebox", () => { const val = {} if (!TypeboxValidator.Check(Default(RawSchema, Clean(RawSchema, val)))) { const errors = [...TypeboxValidator.Errors(val)] } }) bench("ajv", async () => { const val = {} const result = AjvValidator(val) if (!result) { const errors = AjvValidator.errors } }) }) const ajvPkg = await import("ajv/package.json") const typeboxPkg = await import("./node_modules/@sinclair/typebox/package.json") console.log(`ajv: ${ajvPkg.version}`) console.log(`TypeBox: ${typeboxPkg.version}`) await run({ percentiles: false, }) ```And in my local potato machine this is the result:
@SaltyAom I really hope to hear your thoughts about this matter as we are in stage of refactoring our application.