Custom validators

The problem.
My customers frequently upload images of wrong size or type e.g. of too small resolution, vector instead of raster etc.

The desired decision would be to add a custom validators to model fields. I suppose this should be a pretty common problem, but I can’t find any official document on the topic.

So which is the proper way to add a custom validation rules and to show user corresponding error messages in admin area?

1 Like

To achieve some validation on fields you can use lifecycles.

module.exports = {
  lifecycles: {
    // Called before an entry is created
    beforeCreate(data) {
      // calling a custom services which handles the validation process
      // and returns a message if something is wrong and true if everything is ok
      let message = await strapi.services.zzz.validation(data);
      if (message != true){
          throw new Error(message);
       }
    },
  },
};

The problem here is that the image gets uploaded when you select it inside the media field. So when you create a new entry in your collection type the image already exists.

To achieve custom validation during media upload you should use extensions.
The file you need is located here: ./node_modules/strapi-plugin-upload/services/Upload.js. Copy its content to ./extensions/upload/services/Upload.js and add your custom logic inside it.

4 Likes

Thank you!
One more thing. Is it possible to pass the error messages to the client side, so the custom error message would appear near the corresponding input inside the form just like the native validation messages?

File ./node_modules/strapi-plugin-upload/controllers/upload/admin.js, copy to ./extensions/upload/controllers/upload/admin.js, modify the uploadFiles function:

  async uploadFiles(ctx) {
    const {
      state: { userAbility, user },
      request: { body, files: { files } = {} },
    } = ctx;

    const uploadService = strapi.plugins.upload.services.upload;
    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.create,
      fileModel
    );

    if (!pm.isAllowed) {
      throw strapi.errors.forbidden();
    }

    const data = await validateUploadBody(body);
    // ----------------------------------------
    // here I do some validation before the upload process.
    // throw strapi.errors.badRequest('');
    let validationFail = true;
    if (validationFail) {
      throw strapi.errors.badRequest('WRONG SIZE!!11');
    }
    // ----------------------------------------
    const uploadedFiles = await uploadService.upload({ data, files }, { user });

    ctx.body = pm.sanitize(uploadedFiles, { action: ACTIONS.read, withPrivate: false });
  },

Result during upload:

3 Likes

Thank you for a such a detailed answer.

Actually I meant a bit different. Usually particular validation constraints belong to specific fields. I mean for some attributes it’s ok to use svg, in others only raster are required, so validation better be related to specific model attribute instead of all files upload.

What i meant was this type of in-form attribute specific error message Screenshot_2020-11-17 Strapi - Content Manager

Anyway I dug up some code in strapi-plugin-content-manager and in-form error messages like that generated on back-end seem impossible right now.

Actually the error message about uniqueness validation fail (back-end generated) looks like this Screenshot from 2020-11-17 18-50-08 Kind of not really readable to end user :frowning:

Currently, It’s impossible to achieve custom validations(with custom messages) in strapi for a field.

This feature is already added in Strapi’s Roadmap. You can vote for it and left a message here:

1 Like

Hi there!
Any updates on this?
I’m trying to achieve this result but it seems it doesn’t work for me. Following the same steps won’t work.
I’m copying the required files from strapi-plugin-upload to extensions folder, but even so, the upload function used is that from strapi-plugin-upload, not the one just created.

Hello @sunnyson, I have a question, is this process will work when deploying to production? In my case, it’s not. I don’t know why, it only works on the development side.

async uploadFiles(ctx) {
    const {
      state: { userAbility, user },
      request: { body, files: { files } = {} },
    } = ctx;


    const uploadService = strapi.plugins.upload.services.upload;
    const pm = strapi.admin.services.permission.createPermissionsManager({
      ability: userAbility,
      action: ACTIONS.create,
      model: fileModel,
    });


    if (!pm.isAllowed) {
      throw strapi.errors.forbidden();
    }

    const data = await validateUploadBody(body);

    if (ctx.request.header['content-length'] > 10000000) {
      throw strapi.errors.badRequest('Image must not exceed to 10MB');
    }

    const uploadedFiles = await uploadService.upload({ data, files }, { user });

    ctx.body = pm.sanitize(uploadedFiles, { action: ACTIONS.read, withPrivate: false });
  },
};

This is the path that I pasted the code from the admin.js in node_module (strapi-plugin-upload)
extension2

This is the sample that only works on development side but not in production
image

Note: I’m using Cloudinary in media uploading.

I am looking to solve the same issue using Strapi 4.2.x.
Are there any solutions for this issue using Strapi 4.2 and above?

Hi all,

There is actually a pretty easy way to accomplish this in Strapi 4,

The Forms use Yup validation so you can hack into that system to prompt a custom error message on the input itself and also show a global error message while you’re at it.

Just create a lifecycles.js file inside the content-types folder, here is some example code to help you get started

  async beforeUpdate(event) {
    const { data } = event.params;
    const { YupValidationError } = require("@strapi/utils").errors;

    if (data.description) {
      const errorMessage = {
            "inner": [
                {
                    "name": "ValidationError", // Always set to ValidationError
                    "path": 'description', // Name of field we want to show input validation on
                    "message": 'some custom error message', // Input validation message
                }
            ]
        };
      const globalErrorMessage = "You have some issues";
      throw new YupValidationError(errorMessage, globalErrorMessage);
    }

  },```

more info at [Strapi lifecycle hooks docs](https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#lifecycle-hooks)
3 Likes

@sofian-io But the frontend gets only the internal server error for strapi v4.4.7

Hi @Viktar can you share your code, terminal output

An internal server error most likely points to an error in your code, check your terminal output to see get more details.

Yes, sure, as mentioned here also Get 500 error on client side but 403 in terminal for the backend - #8 by smich

The errors middleware turns all the Errors to internal server error, but should not. Terminal is good, but I would like the UI to get the right validation error

strapi version 4.4.7, same code is working for strapi 4.1.something

and my code was copied from your snippet, so it is basically the same