elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
9.81k stars 208 forks source link

Consider using ajv instead of typebox for schema validation #774

Open ayZagen opened 1 month ago

ayZagen commented 1 month ago

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:

To conform all above points with typebox you must do following:

import { Clean, Default } from "@sinclair/typebox/value"

// assuming `Schema` is compiled with TypeCompiler
if( !Schema.Check( Default( RawSchema, Clean(RawSchema, value) ) ){
  const errors = [...Schema.Error(value)]
}

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 to ajv internally. End-Users will not notice that much difference.

ajv is not a new guy in the town and it is used internally by fastify.

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:

$ bun run bench.ts 
ajv: 8.17.1
TypeBox: 0.33.5
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
runtime: bun 1.1.24 (x64-linux)

benchmark      time (avg)             (min … max)
-------------------------------------------------
• json-schema
-------------------------------------------------
typebox     3'260 ns/iter   (1'833 ns … 5'491 µs)
ajv           296 ns/iter     (211 ns … 2'261 ns)

summary for json-schema
  ajv
   11.01x faster than typebox

@SaltyAom I really hope to hear your thoughts about this matter as we are in stage of refactoring our application.

lilprs commented 4 weeks 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
ayZagen commented 4 weeks ago

@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.

antn9x commented 2 weeks ago

May we have more data validation library for elysia? I'd like to work with ajv too, it's simple and performance.

BleedingDev commented 1 week ago

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. :)