admin管理员组

文章数量:1178538

According to Joi documentation, you can use Joi.object() like so:

const object = Joi.object({
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
});

But you can also write an equivalent code using Joi.object().keys() like so:

const object = Joi.object().keys({
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
});

What's the difference between the two?

According to Joi documentation, you can use Joi.object() like so:

const object = Joi.object({
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
});

But you can also write an equivalent code using Joi.object().keys() like so:

const object = Joi.object().keys({
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
});

What's the difference between the two?

Share Improve this question asked Nov 12, 2019 at 3:17 BerryBerry 2,2985 gold badges26 silver badges49 bronze badges 1
  • 1 The two are not equivalent. The first example defines a single schema that allows only a and b. The second example with object() uses Joi.any() as the initial schema and then extends it by adding definitions for a and b while presumably inheriting any's permissive schema definition. You probably don't want to extend any unless you know you're handling open-ended input. – Dai Commented Nov 12, 2019 at 3:25
Add a comment  | 

4 Answers 4

Reset to default 14

If you're writing your schema once then you do not need to use .keys(). As their docs say it is "useful" to use .keys() when added more lines (keys) to your object.

Joi.object().keys([schema]) notation

This is basically the same as Joi.object([schema]), but using Joi.object().keys([schema]) is more useful when you want to add more keys (e.g. call keys() multiple times). If you are only adding one set of keys, you can skip the keys() method and just use object() directly.

Some people like to use keys() to make the code more explicit (this is style only).

Taken from: https://github.com/hapijs/joi/blob/v8.0.3/API.md#joiobjectkeysschema-notation


I also found this:

There are many ways to use joi. The hapi docs can't show everything. Calling keys() is only recommended when adding keys to an object as it creates another schema

Taken from: https://github.com/hapijs/hapi/issues/2222

Like the documentation states:

object.keys([schema])

Sets OR extends the allowed object keys where:

  • schema - optional object where each key is assigned a joi type object. If schema is {} no keys allowed. If schema is null or undefined, any key allowed. If schema is an object with keys, the keys are added to any previously defined keys (but narrows the selection if all keys previously allowed).

Thus by calling Joi.object() you first create a schema that allows any keys and then by calling .keys([schema]) you extend that schema (basically the same as defining a new schema with Joi.object([schema]) )

So these two are equivalent:

const a = Joi.object({ firstName: Joi.string() });
const b = Joi.object().keys({ firstName: Joi.string() });

You can also extend both schemas created above:

const aExtended = a.keys({ lastName: Joi.string() })
const bExtended = b.keys({ lastName: Joi.string() })

Which one to use then?

Like stated in the previous answers, sometimes also top level schemas are created using .keys() for code consistency reasons, but in the end I think it's a matter of personal preference.

The @hapi/joi documentation is not very clear on this (at v17.1.0). The resulting schemas have the same value, and they validate the same. Looking at the source, Object type is a Keys type with only a change that object need not copy keys from the Any type it is defined from.

Welcome to Node.js v12.16.1.
Type ".help" for more information.
> const Joi = require('@hapi/joi')
undefined
> const util = require('util')
undefined
> const object1 = Joi.object({
...     a: Joi.number().min(1).max(10).integer(),
...     b: Joi.any()
... });
undefined
> const object2 = Joi.object().keys({
...     a: Joi.number().min(1).max(10).integer(),
...     b: Joi.any()
... });
undefined
> util.format(object1) == util.format(object2)
true
> object1.validate({a: 1, b: 1})
{ value: { a: 1, b: 1 } }
> object2.validate({a: 1, b: 1})
{ value: { a: 1, b: 1 } }
> object1.validate({a: 0})
{
value: { a: 0 },
error: [Error [ValidationError]: "a" must be larger than or equal to 1] {
    _original: { a: 0 },
    details: [ [Object] ]
}
}
> object2.validate({a: 0})
{
value: { a: 0 },
error: [Error [ValidationError]: "a" must be larger than or equal to 1] {
    _original: { a: 0 },
    details: [ [Object] ]
}
}
> object1.validate({a: 1, b: 1, c:1})
{
value: { a: 1, b: 1, c: 1 },
error: [Error [ValidationError]: "c" is not allowed] {
    _original: { a: 1, b: 1, c: 1 },
    details: [ [Object] ]
}
}
> object2.validate({a: 1, b: 1, c:1})
{
value: { a: 1, b: 1, c: 1 },
error: [Error [ValidationError]: "c" is not allowed] {
    _original: { a: 1, b: 1, c: 1 },
    details: [ [Object] ]
}
}
> object1.validate({a: 1})
{ value: { a: 1 } }
> object2.validate({a: 1})
{ value: { a: 1 } }
> object1.validate({b: 1})
{ value: { b: 1 } }
> object2.validate({b: 1})
{ value: { b: 1 } }
> object1.validate({})
{ value: {} }
> object2.validate({})
{ value: {} }

Difference between the .append(schema) and .keys(schema) is also unclear in documentation. The .append(schema) does not create a new copy if schema is empty, but otherwise it just returns the value from .keys(schema). I found no example where this would make a difference.

> util.format(Joi.object({}).keys({a:1})) == util.format(Joi.object({}).append({a:1}))
true
> util.format(Joi.object({}).unknown().keys({a:1})) == util.format(Joi.object({}).unknown().append({a:1}))
true

We can also define schema without using keys() when there is only one set of keys define our schema directly within the object() generation, like so:

const schema = Joi.object({
    username: Joi.string().alphanum().min(3).max(16).required(),
    password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/).min(6).required()
}).with('username', 'password');

So why use keys() with a single key set?

To keep your code consistent. Throughout the Joi documentation keys() are used throughout, even on single key objects.

Using keys()

As we mentioned before, keys() does not have to be used if we are defining a single key set. Additionally, if no keys are defined on a Joi.object(), then any key will be valid: there are no rules to invalidate any object we test with our Joi schema. We also have the option to add keys after the schema’s original definition. The following example taken from the Joi documentation demonstrates this:

//define base object
const base = Joi.object().keys({
    a: Joi.number(),
    b: Joi.string()
});
// add a c key onto base schema
const extended = base.keys({
    c: Joi.boolean()
});

As you may have noticed, we are defining constants here. Joi objects are immutable, so extending a base schema will result in a completely new object. Here we have saved that object as the constant extended. We have also introduced the Joi.boolean() rule above, which comes in handy for testing checkboxes and other switches, where we expect a true or false value.

本文标签: javascriptWhat is the difference between Joiobject() and Joiobject()keys()Stack Overflow