Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to use TypeBox TypeCompiler #23

Closed
2 tasks done
sinclairzx81 opened this issue Jul 6, 2022 · 3 comments
Closed
2 tasks done

Option to use TypeBox TypeCompiler #23

sinclairzx81 opened this issue Jul 6, 2022 · 3 comments

Comments

@sinclairzx81
Copy link
Contributor

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

As of TypeBox 0.24.0, this library now offers a specialized TypeCompiler that builds highly optimized validation routines specifically for TypeBox types. To allow users to make use of the compiler, this proposal seeks to expose an TypeBoxValidatorCompiler export that users can optionally set to override the default AJV compiler.

The anticipated usage may look as follows.

import { Type, TypeBoxTypeProvider, TypeBoxValidatorCompiler } from '@fastify/type-provider-typebox'
import Fastify from 'fastify'

const fastify = Fastify().setValidatorCompiler(TypeBoxValidatorCompiler)

fastify.withTypeProvider<TypeBoxTypeProvider>().get('/', {
  schema: {
    querystring: Type.Object({
      x: Type.String(),
      y: Type.Number(),
      z: Type.Boolean()
    })
  }
}, (req) => { ... })

The TypeBoxValidatorCompiler import should viewed as optional an extra from a user (and documentation) standpoint. The compiler itself only builds for a subset of the JSON schema specification. Without configuration, this provider will continue to work with AJV.

Additional Information on the compiler can be found https://github.com/sinclairzx81/typebox#Compiler

Submitting for consideration

Motivation

The following are comparative performance benchmarks measured against TypeBox and AJV for a variety of TypeBox types. Each test iterates several million times and measures the elapsed time to complete benchmark.

The Results

┌──────────────────┬────────────┬───────────────┬───────────────────┬─────────────┐
     (index)       Iterations  Ajv Completed  TypeBox Completed  Performance 
├──────────────────┼────────────┼───────────────┼───────────────────┼─────────────┤
       Any          16000000      '244ms'          '147ms'         '+65%'    
     Boolean        16000000      '257ms'          '150ms'         '+71%'    
     Integer        16000000      '253ms'          '154ms'         '+64%'    
       Null         16000000      '244ms'          '148ms'         '+64%'    
      Number        16000000      '245ms'          '145ms'         '+68%'    
      String        16000000      '246ms'          '152ms'         '+61%'    
     Unknown        16000000      '239ms'          '170ms'         '+40%'    
      RegEx         16000000      '659ms'          '528ms'         '+24%'    
     ObjectA        16000000      '441ms'          '281ms'         '+56%'    
     ObjectB        16000000      '664ms'          '462ms'         '+43%'    
      Tuple         16000000      '295ms'          '189ms'         '+56%'    
      Union         16000000      '299ms'          '209ms'         '+43%'    
    Recursive       16000000     '5221ms'         '1851ms'         '+182%'   
     Vector4        16000000      '291ms'          '168ms'         '+73%'    
     Matrix4        16000000      '589ms'          '377ms'         '+56%'    
 Literal<String>    16000000      '247ms'          '151ms'         '+63%'    
 Literal<Number>    16000000      '240ms'          '148ms'         '+62%'    
 Literal<Boolean>   16000000      '243ms'          '149ms'         '+63%'    
  Array<Number>     16000000      '422ms'          '223ms'         '+89%'    
  Array<String>     16000000      '422ms'          '300ms'         '+40%'    
  Array<Boolean>    16000000      '474ms'          '341ms'         '+39%'    
  Array<ObjectA>    16000000     '3517ms'         '2027ms'         '+73%'    
  Array<ObjectB>    16000000     '6335ms'         '4590ms'         '+38%'    
   Array<Tuple>     16000000     '1404ms'         '1040ms'         '+35%'    
  Array<Vector4>    16000000     '1462ms'          '751ms'         '+94%'    
  Array<Matrix4>    16000000     '6239ms'         '4150ms'         '+50%'    
└──────────────────┴────────────┴───────────────┴───────────────────┴─────────────┘

Example

The following should be the full implementation of the TypeBoxValidatorCompiler. However additional updates may include formatting validation errors to present better as HTTP response messages.

import Fastify from 'fastify'
import { FastifySchemaCompiler, FastifyTypeProvider } from "fastify"
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { Type, Static, TSchema } from '@sinclair/typebox'

export * from '@sinclair/typebox' // review: Ensure Type, Static and other associative types are exported

/** TypeBoxValidatorCompiler */
export const TypeBoxValidatorCompiler: FastifySchemaCompiler<TSchema> = ({ schema }) => {
    const TypeCheck = TypeCompiler.Compile(schema)
    return (data): any => {
        if (TypeCheck.Check(data)) return // ok
        const errors = [...TypeCheck.Errors(data)]
        throw Error(errors.map(({ message, path }) => `${message} for ${path}`).join('. '))
    }
}

/** TypeBoxTypeProvider */
export interface TypeBoxTypeProvider extends FastifyTypeProvider {
    output: this['input'] extends TSchema ? Static<this['input']> : never
}
@mcollina
Copy link
Member

mcollina commented Jul 7, 2022

Wow, this looks truly amazing! Are you going to send a PR for this?

@sinclairzx81
Copy link
Contributor Author

sinclairzx81 commented Jul 8, 2022

@mcollina Sure. See associated PR which implements this feature. Have linked associated benchmarks in the PR. Cheers! :)

@sinclairzx81
Copy link
Contributor Author

@mcollina Will close off this issue. Many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants