ajv-validator / ajv

The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
https://ajv.js.org
MIT License
13.88k stars 878 forks source link

Concrete example of using standalone at runtime #1831

Open rehanvdm opened 3 years ago

rehanvdm commented 3 years ago

What version of Ajv you are you using? 8.8.2

What problem do you want to solve? I do not know how to use the validate_schema.js at runtime to validate the schema? The https://ajv.js.org/standalone.html

What do you think is the correct solution to problem? Update the docs or create an example somewhere that clearly distinguishes between generating the JS files and consuming them for validation.

Will you be able to implement it? Maybe if someone can give me a rough example?

rehanvdm commented 3 years ago

@epoberezkin do you maybe know of anything? Or can maybe provide me with something? I would like to write a blog post to highlight the standalone mode, especially in the context of using it on AWS Lambda and comparisons between other types of type safety initiatives like class-transformer etc.

This piqued my interest https://github.com/moltar/typescript-runtime-type-benchmarks and I am sure that AJV standalone will blow all of them out of the water.

epoberezkin commented 3 years ago

I do not know how to use the validate_schema.js at runtime to validate the schema?

This file is a CJS module from which you can simply require a validation function or import default export - if you compile any sample schema you will see.

Alternatively, you can generate a module that exports multiple functions, in which case you can either require and object with all these functions or use normal import declarations.

The latter is what is done in @gajus's table package: https://github.com/gajus/table/blob/master/src/types/generated/validators.d.ts

This type definitions are for the validators generated with this script: https://github.com/gajus/table/blob/master/package.json#L73

There is a proposal to make exports more aligned with other import specifications: https://github.com/ajv-validator/ajv/issues/1523

I do think it should be done, but I am just not finding the time for it - may some time over Xmas :) Or maybe you can do it - it's not that difficult to be honest to render a different module format based on some Ajv option - the actual code will be the same.

I would like to write a blog post to highlight the standalone mode, especially in the context of using it on AWS Lambda and comparisons between other types of type safety initiatives like class-transformer etc.

Exciting! I don't know if anybody did it, but it's not documented here how to do it, so it is great. See this issue for example: https://github.com/ajv-validator/ajv/issues/1386. I would definitely reference the post from the docs if you write it and it's not too complex :)

https://github.com/moltar/typescript-runtime-type-benchmarks

Ha! That might be... If you're interested in benchmarking Ajv, there is another thing I never came around to doing – benchmarking Ajv JTD parsers/serializers against alternatives (other than JSON). See these issues:

https://github.com/fastify/fastify/issues/1934

https://github.com/fastify/fast-json-stringify/pull/362 - looks like it was done, but I've never seen the results...

rehanvdm commented 3 years ago

Thanks, I actually found that example last night (and stayed up way too late), I think you mentioned it another ticket.

I was initially confused because if you only have 1 type, then then it does not export by name as it does if you have multiple, so I couldn't understand how to get it to be named. That example helped and I got it working.

On the CJS vs ESM, I also ran into that wall. After creating a .d.ts file for TSC to correctly understand it, it worked. But I had to create the decelerations by hand. Some quick googling (probably not, as mentioned long night) I found that the TSC can generate them with this command.

tsc --declaration -allowJs --emitDeclarationOnly \"ajv-standalone/schemas/validators.js\" --outDir \"ajv-standalone/schemas\"

But the declaration was kinda wrong and TSC was still not happy. Actually, I am using ES Build, similar to the others using WebPack and Rollup. I played around by adding the .cjs extension to the generated JS file but then the declaration that is generated has the wrong extension, some manual intervention and variations later got it working. Still not really happy with it, there must be something that can make the generated CJS play nice with TS and all these build systems.

With the benchmarking, I just did a very unscientific test to see how many validations can be done by a few different libraries. I benchmarked it against plain JS validation (simple typeof operations), which isn't very runtime type-safe, I just wanted to get a benchmark of what "pure" JS validation does without reflection. On my machine, this was about 12M validations a second, class-validator got 2M validations per second and marshal or now renamed to @deepkit/type got 7M.

AJV in standard mode got around 9M as well, and the stand alone mode also got around 9M validations a second. I knew that the standalone optimizes for that initial load, which will certainly help in a serverless/lambda environment. But I was hoping that the standalone mode would produce "close to pure JS" code, by that I mean fewer function calls, evaluations, cycles etc. I was a little bit disappointed when I got that result, I hoped standalone would also do faster evaluations at runtime. Does this result sound about right to you?

I was also curious about Ajv JTD and compared it against itself with the normal compile mode and both had roughly the same amount of validations per second.

I will still write something up that is more concrete with code samples. Just need to first crack the case of letting the generated CJS file play nice with TS and the build systems.

rehanvdm commented 2 years ago

@epoberezkin I wrote that blog post =>https://www.rehanvdm.com/blog/typescript-type-safety-with-ajv-standalone of how I am now using the esm option, the TL;DR

TypeScript does a great job at compile time type safety, but we still need to do runtime checks just like in JavaScript. There are many packages and tools to help with this, we focused on AJV Standalone that outputs JS validation functions at compile time to be used at runtime. Going from TS Types to JSON Schema to JS functions allows us to validate TS Types where the other packages all work with classes and reflection.

rehanvdm commented 2 years ago

I also added AJV to that bench marking repo, AJV is one of the fastest => https://github.com/moltar/typescript-runtime-type-benchmarks#node-14x-json-csv-svg

epoberezkin commented 2 years ago

I also added AJV to that bench marking repo, AJV is one of the fastest

Both suretype and ts-json-validator use ajv to compile JSON schemas to JavaScript, that’s why benchmarks are nearly identical (the difference is probably just a noise or maybe different ajv settings used)… Marshal does something different - it says something about Ruby types in the docs