'how to manipulate Joi messages by http request?

I have a user schema like the code below:

const Joi = require("joi");
const message = require("./types/string");

const username = Joi.string()
  .regex(/^[A-Za-z0-9]+$/)
  .required()
  .min(5)
  .max(20)
  .messages(message);

const email = Joi.string()
  .regex(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/)
  .required()
  .min(5)
  .max(255)
  .email()
  .messages(message);

const password = Joi.string()
  .regex(/^[A-Za-z0-9]+$/)
  .required()
  .min(5)
  .max(255)
  .messages(message);

const userSchema = Joi.object({
  username,
  password,
  email,
});

exports.userSchema = { route: "/users", object: userSchema };

and a schema validation like the code below that I have found at https://www.digitalocean.com/community/tutorials/how-to-use-joi-for-node-api-schema-validation:

const _ = require("lodash");
const { StatusCodes } = require("http-status-codes");
const { Schemas } = require("../joi-schema-validation");

exports.schemaValidator = function (useJoiError = false) {
  const _useJoiError = _.isBoolean(useJoiError) && useJoiError;
  const _supportedMethods = ["post", "put", "patch"];

  const _validationOptions = {
    abortEarly: false,
    allowUnknown: true,
    stripUnknown: true,
  };

  return (req, res, next) => {
    const route = req.route.path;
    const method = req.method.toLowerCase();

    const schema = Schemas.find(
      (schema) => schema.route === route.replace("/:id", "")
    );

    if (_.includes(_supportedMethods, method) && schema) {
      const { object } = schema;
      const { error, value } = object.validate(req.body, _validationOptions);

      if (error) {
        const JoiError = {
          status: "failed",
          validationErrors: {
            details: _.map(error.details, ({ message, context }) => ({
              [context.label]: req.t(message.replace(/['"]/g, "")),
            })),
          },
        };

        const CustomError = {
          status: "failed",
          error: "Invalid request data. Please review request and try again.",
        };

        return res
          .status(StatusCodes.BAD_REQUEST)
          .json(_useJoiError ? JoiError : CustomError);
      } else {
        req.body = value;
        return next();
      }
    }
    return next();
  };
};

Let's say I have two locales joi messages, one in english and one in spanish. How can I switch to locale joi messages if "Accept-Language" header is "es-es" lcid?



Solution 1:[1]

After digging around and slept over it, I've manage to come up with an easy solution. In the beginning of the route handler middleware, i have a "messages" variable where I check the request header accept language. If the language LCID is "en" then I return the english error messages or return default error messages, in my case the dutch language error messages. Before validating, I pass the return error messages into the messages method of my schema and then chain the validate function to it.

The updated code looks now like code underneath:

const _ = require("lodash");
const { StatusCodes } = require("http-status-codes");
const { Schemas } = require("../joi-schema-validation");

exports.schemaValidator = function (useJoiError = false) {
  const _useJoiError = _.isBoolean(useJoiError) && useJoiError;
  const _supportedMethods = ["post", "put", "patch"];

  const _validationOptions = {
    abortEarly: false,
    allowUnknown: true,
    stripUnknown: true,
  };

  return (req, res, next) => {
    const messages =
      req.headers["accept-language"] === "en"
        ? require("../joi-schema-validation/types/string")
        : require("../joi-schema-validation/types/string-nl");
        
    const route = req.route.path;
    const method = req.method.toLowerCase();

    const schema = Schemas.find(
      (schema) => schema.route === route.replace("/:id", "")
    );

    if (_.includes(_supportedMethods, method) && schema) {
      const { object } = schema;
      const { error, value } = object
        .messages(messages)
        .validate(req.body, _validationOptions);

      if (error) {
        const JoiError = {
          status: "failed",
          validationErrors: {
            details: _.map(error.details, ({ message, context }) => ({
              [context.label]: message.replace(/['"]/g, ""),
            })),
          },
        };

        const CustomError = {
          status: "failed",
          error: "Invalid request data. Please review request and try again.",
        };

        return res
          .status(StatusCodes.BAD_REQUEST)
          .json(_useJoiError ? JoiError : CustomError);
      } else {
        req.body = value;
        return next();
      }
    }
    return next();
  };
};

If there are any comments or alternatives for this solution, I'm more than happy to read about them.

Happy coding

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dafny