Search code examples
javascriptvalidationjoi

What's the difference between Joi.object().keys() and append() methods in terms of adding more keys


I have this piece of code

const baseSchema = Joi.object.keys({
   name: Joi.string().required()
})

Now I want to add more keys to this schema, I can write

const basicInfoSchema = baseSchema.keys({
   address: Joi.string().required(),
   phoneNumber: Joi.number().required()
})

or

const basicInfoSchema = baseSchema.append({
   address: Joi.string().required(),
   phoneNumber: Joi.number().required()
})

What's the difference between the two?


Solution

  • append has a generic parameter to give some typing to the expected object. Like Joi.object<>() does. keys has no generic parameter.

    Also looking at the source code bellow (version 17.6.0), you can see append calls keys under the hood. I won't paste the documentation, but you can read it there https://joi.dev/api/?v=17.6.0 .

    Basically, append only do the else clause of keys method. While keys can also "Allow all" (any key allowed) or "Allow none" (no keys allowed) of the existing keys. keys has two extra functionalities.

            append: {
                method(schema) {
    
                    if (schema === null ||
                        schema === undefined ||
                        Object.keys(schema).length === 0) {
    
                        return this;
                    }
    
                    return this.keys(schema);
                }
            },
    
            keys: {
                method(schema) {
    
                    Assert(schema === undefined || typeof schema === 'object', 'Object schema must be a valid object');
                    Assert(!Common.isSchema(schema), 'Object schema cannot be a joi schema');
    
                    const obj = this.clone();
    
                    if (!schema) {                                      // Allow all
                        obj.$_terms.keys = null;
                    }
                    else if (!Object.keys(schema).length) {             // Allow none
                        obj.$_terms.keys = new internals.Keys();
                    }
                    else {
                        obj.$_terms.keys = obj.$_terms.keys ? obj.$_terms.keys.filter((child) => !schema.hasOwnProperty(child.key)) : new internals.Keys();
                        for (const key in schema) {
                            Common.tryWithPath(() => obj.$_terms.keys.push({ key, schema: this.$_compile(schema[key]) }), key);
                        }
                    }
    
                    return obj.$_mutateRebuild();
                }
            },