ampersand-state icon indicating copy to clipboard operation
ampersand-state copied to clipboard

Feature idea: Using Joi for prop definition and validation, or adding Joi-like features

Open dminkovsky opened this issue 9 years ago • 13 comments

Have you guys used Joi? I use Joi on the server-side with Hapi and really love it.

I just got started with Ampersand and also really, really love ampersand-state. What a great tool. I was just in the middle of defining some properties on a view (!!!) and wanted to define valid values for one properties a function of the value of another property. Joi lets you do this with alternatives and references.

If Joi was adopted for properties, it could then also be used for validation. It would be a great win, I think. Defining a properties schema already gets you quite close to using that schema for validation.

This wouldn't be trivial, of course. But I wanted to get this out there to see what you think. Joi is apparently browser-compatible via browserify, though they don't run browser tests and they support things like Buffers. So, Joi may be too heavy for this purpose, and if so, perhaps just the above-mentioned reference/alternatives features could be added. And validation via properties' schemas, I think, would be great too.

Thank you.

dminkovsky avatar Sep 17 '14 02:09 dminkovsky

:+1: for Joi integration. We are going to be implementing a validate function that uses Joi, but it would be amazing if we could get it automatically through the typing system.

phated avatar Sep 17 '14 06:09 phated

This would be a wonderful thing as a mixin, great idea.

dazld avatar Sep 26 '14 10:09 dazld

So I just had a quick look:

To have this in core is going to be asking a lot due to file size, and even as a mixin, it's something you might want to be wary of, bundling joi with browserify gives me a 195kB file (unminified), the main pieces being:

From node core:

  • Buffer: 30kB (though depending what else you have in your app you may be pulling this in already
  • util: 15kB (as above)

Joi

  • Joi itself: 68kB
  • Hoek: 17kB
  • isemail: 48kB (!!!)

To be clear, I love Joi for hapi stuff, and it'd be great to see it in the browser, but that's quite a bit of overhead.

latentflip avatar Sep 26 '14 10:09 latentflip

For some comparison, the current sample app generated by the CLI comes in at 250kB all in.

latentflip avatar Sep 26 '14 11:09 latentflip

Using it on the server wouldn't have such problems, but dead right about client side bundle size.

Not even sure the isemail thing would work 100% client side - seeing as it can de dns searches to validate a mail. I guess there might be a client side version of it which you can shim in at build stage, or if not, wouldn't be that hard to whip one up... maybe.

dazld avatar Sep 26 '14 11:09 dazld

Yeah, if joi is open to being made more suitable for the browser, you could add browser specific alternatives and use browserifies "browser" config in the joi package.json to switch them out.

Philip Roberts &yet

On 26 Sep 2014, at 12:42, Dan Peddle [email protected] wrote:

Using it on the server wouldn't have such problems, but dead right about client side bundle size.

Not even sure the isemail thing would work 100% client side - seeing as it can de dns searches to validate a mail. I guess there might be a client side version of it which you can shim in at build stage, or if not, wouldn't be that hard to whip one up... maybe.

— Reply to this email directly or view it on GitHub.

latentflip avatar Sep 26 '14 12:09 latentflip

Yeah I was thinking size is a big issue. And given that the Hapi team is very back-end centric, I don't see them expending effort to make a browser appropriate build (without isemail, Buffer support, etc). So unless that happens, it's probably too much of a stretch with Joi.

That said, the way State is looking now, it seems perfect to mesh property defs and validation somehow.

dminkovsky avatar Sep 26 '14 14:09 dminkovsky

Related: there's https://github.com/fhemberger/joi-browserify, but it's almost 2MB minified (yes, minified!!!).

mik01aj avatar Apr 21 '15 10:04 mik01aj

:+1: sharing of model validation on the client and server would be very powerful.

zpratt avatar May 07 '15 03:05 zpratt

@mik01aj when you build joi-browserify currently it actually isn't minified and that includes inline source maps. Look at what the build.js is doing. He should not have named it min.js but I guess he had an issue with minifiyify and just commented that part out.

Anyway, if you turn debugging off (disabling inline source maps), the uminified size for joi-browserify is 729K, minified it is 362K (using uglify-js), and minified and gzipped 113K.

Conversely, full joi minified and gzipped comes in at 166K.

So joi-browserify helped some, but would be nice if it could be squeezed more. I didn't try using google-closure, to see what it would reduce it to.

jeffbski avatar May 12 '15 22:05 jeffbski

This would be doable as a dataType mixin with a minor non-breaking change to dataType.set and def.test.

original

            // check type if we have one
            if (dataType && dataType.set) {
                cast = dataType.set(newVal);
                newVal = cast.val;
                newType = cast.type;
            }

            // If we've defined a test, run it
            if (def.test) {
                err = def.test.call(this, newVal, newType);
                if (err) {
                    throw new TypeError('Property \'' + attr + '\' failed validation with error: ' + err);
                }
            }

becomes

            // check type if we have one
            if (dataType && dataType.set) {
                cast = dataType.set(newVal, attr, def); // << adds `attr` and `def` to `.set` call
                newVal = cast.val;
                newType = cast.type;
            }

            // If we've defined a test, run it
            if (typeof def.test === 'function') { // << adds `typeof` test so we can pass something other than a function (like a Joi schema) and not have it blow up here but it is usable within the dataType mixin
                err = def.test.call(this, newVal, newType);
                if (err) {
                    throw new TypeError('Property \'' + attr + '\' failed validation with error: ' + err);
                }
            }

I ran a quick spike and this worked perfectly. I was able to create and use a Joi dataType mixin. The benefit of this change is that it allow much more powerful dataType mixing even outside of Joi. Further, the above changes does not break anything and the tests continue to pass.

If this looks good, I'll make it a PR.

wilmoore avatar Jun 10 '16 19:06 wilmoore

@wilmoore I like those changes, they are minor and non-breaking.

Can we get some feedback from other Joi users? if this change is good for you, than let's get a PR ready

pgilad avatar Jun 10 '16 20:06 pgilad

I built the-thing-is a while back which provides joi-like definitions without the bloat. the-thing-is and it's one dependency, is-too, are under 20kb combined before minification.

Here's an example of how to define and use a schema. It could fit in with Ampersand State, I've just never tried it.

var the = require('the-thing-is')

var a_valid_user = {
  name: ['string'],
  address: {
    street1: ['present', 'string', {matches: /.*/}],
    street2: ['string'],
    city: ['present', 'string'],
    state: ['present', 'string', {matches: /^[A-Z]{2}$/}],
    zip: ['present', 'string', {matches: /^[0-9]{5}$/}]
  }
}

var user = {
  name: "Joe Bob",
  address: {
    street1: '123 Any St.',
    street2: '',
    city: 'Anytown',
    state: undefined,
    zip: 12345
  }
}

the(user).is(a_valid_user) // false
console.log(the.last.error) 
// [
//   { 'address.state': ['present'] },
//   { 'address.zip': ['string'] }
// ]

mrDarcyMurphy avatar Jun 15 '16 17:06 mrDarcyMurphy