Loki
Loki

Reputation: 1205

Joi validator validateAsync remove warnings

I have been using Joi library for validation user inputs like this.

const Joi = require('joi');

const schema = Joi.object({
    username: Joi.string()
        .alphanum()
        .min(3)
        .max(30)
        .required()
});

const validationResult = await schema.validateAsync({ username: 'a' }, { abortEarly: false, warnings: true });

This works fine, the problem is later in code I would want to use this validation again. But the warning messages are still in the local context.

And if I input he { username: 'ba'} the error message will still display that min length of "a" needs to be 3.

Do you know how can I use the schema object again, and strip the local warnings context.

Upvotes: 1

Views: 59

Answers (1)

Zeros-N-Ones
Zeros-N-Ones

Reputation: 1082

Validation context in Joi can carry over between validations when reusing the same schema instance. Try these two approaches and see if it solves the problem:

Create fresh schema clone for each validation:

const baseSchema = Joi.object({
    username: Joi.string()
        .alphanum()
        .min(3)
        .max(30)
        .required()
});

// Use schema.clone() for each validation
const validationResult1 = await baseSchema.clone().validateAsync({ username: 'a' });
const validationResult2 = await baseSchema.clone().validateAsync({ username: 'ba' });

Create a validation function that always uses a fresh schema:

const validateUsername = async (data) => {
    const schema = Joi.object({
        username: Joi.string()
            .alphanum()
            .min(3)
            .max(30)
            .required()
    });
    
    return await schema.validateAsync(data, { abortEarly: false, warnings: true });
};

// Use it multiple times
try {
    await validateUsername({ username: 'a' });
    await validateUsername({ username: 'ba' });
} catch (err) {
    console.error(err);
}

The second approach is generally preferred because:

  • It ensures a clean validation context every time
  • It encapsulates the validation logic in a reusable function
  • It's more explicit about the validation being performed
  • It prevents any potential memory leaks from storing validation state

I hope this helps

UPDATED TO ADDRESS NEW ISSUE:

You're caching the schema in a Map but still running into the same context persistence issue. The solution below allows for efficient caching while avoiding the context issue.

const Joi = require('joi');

// Create a schema cache using Map
class ValidationSchemaCache {
    constructor() {
        this.schemaCache = new Map();
    }

    getSchema(schemaName) {
        if (!this.schemaCache.has(schemaName)) {
            // Define your schemas here
            const schemas = {
                'user': Joi.object({
                    username: Joi.string()
                        .alphanum()
                        .min(3)
                        .max(30)
                        .required()
                })
                // Add other schemas as needed
            };

            const schema = schemas[schemaName];
            if (!schema) {
                throw new Error(`Schema ${schemaName} not found`);
            }

            this.schemaCache.set(schemaName, schema);
        }

        // Return a clone of the cached schema
        return this.schemaCache.get(schemaName).clone();
    }

    async validate(schemaName, data, options = { abortEarly: false, warnings: true }) {
        const schema = this.getSchema(schemaName);
        return await schema.validateAsync(data, options);
    }
}

// Usage example:
const validationCache = new ValidationSchemaCache();

async function test() {
    try {
        // First validation
        const result1 = await validationCache.validate('user', { username: 'a' });
        console.log('Result 1:', result1);
    } catch (err1) {
        console.error('Error 1:', err1.details);
    }

    try {
        // Second validation with different data
        const result2 = await validationCache.validate('user', { username: 'ba' });
        console.log('Result 2:', result2);
    } catch (err2) {
        console.error('Error 2:', err2.details);
    }
}

test();

What I’ve done here is:

  • Create a ValidationSchemaCache class that manages our schemas
  • The schemaCache Map stores the base schema definitions
  • The getSchema method always returns a clone of the cached schema using clone()
  • The validate method provides a convenient way to validate data using a cached schema

Some of the pros of using this approach:

  • Schemas are cached for memory efficiency
  • Each validation gets a fresh clone, preventing context pollution
  • Easy to add and manage multiple schemas
  • Consistent validation interface
  • Maintains proper error messages for each validation attempt

You may expand this further by:

  • Adding more schema definitions
  • Implementing schema versioning
  • Adding custom validation options per schema type
  • Adding validation result transformation methods

Upvotes: 1

Related Questions