RomanHDev
RomanHDev

Reputation: 104

Validate Mongoose Schema against external API

I have a mongoose schema like the following:

const User: Schema = new Schema({
// some other fields
email: {type: String, unique: true, require: true, validate: [myValidator, 'invalid email provided'],
// some more fields
)};

My myValidator uses validatorJS isEmail function to check if a valid email address has been entered and looks like this:

function myValidator(email: String) {
   return isEmail(email: String, {require_tld: true, domain_specific_validation: true});

So far all of this works as expected. But now I have the following problem, which might be linked to my limited understanding of Typescript/JaveScript. I would like to extend myValidator to check the entered email address against an external API.

So far I've tried something like this:

function myValidator(email: String) {
     let isValid = false;
     request('https://disposable.debounce.io/?email=' + email, { json:     true }, async (err, res, body) => {
     if (err) { return console.log(err); }
     isValid = body.disposable;
     });
     if (isValid && !isEmail(email, {require_tld: true, domain_specific_validation: true})) {
     return false;
}

But this obviously fails as request is an async operation so isValid will always be false

So I 'attempted' to make this function an async/await function.

function myValidator(email: String) {
  const remoteCheck = function() {
    return new Promise(function() {
      request('https://disposable.debounce.io/?email=' + email, { json: true }, async (err, res, body) => {
          if (err) { return console.log(err); }
          return body.disposable;
        });
    });
  };
  async function f() {
    const isValid = await remoteCheck();
    if (isValid === true) {
      return false;
    } else {
      isEmail(email, {require_tld: true, domain_specific_validation: true});
      }
    }
    if(f()) {
    return true; 
    } else {
    return false;
  }

But this gives me an error, because this function is not assignable to validate. Please note that this is my first attempt on async/await functions. Could somebody point out what I'm missing here?

UPDATE: So after I've spend a whole day now to familiarize myself with the concepts of callbacks and promises I came up with a working solution. I'll provide it as an answer below

Upvotes: 0

Views: 405

Answers (1)

RomanHDev
RomanHDev

Reputation: 104

I checked SO and I couldn't find a solution to this. So if anybody comes across this issue again, I'll provide a working solution for this.

If you want to validate a field against an external data-source, try the following. This validator will check the email-address provided against an API (you can of course use any other API as well) that checks if the address is a DEA and also use ValidatorJS isEmail to check for syntax:

const YourSchema = new Schema({
// your fields
})

SchemaName.path('fieldNameToCheck').validate({
    validator: function(value) {

    return new Promise(function (resolve, reject) {

    request.get('https://disposable.debounce.io/?email=' + value, function (error, response, data) {

      if (error) reject(error);
      const content = JSON.parse(data);
      resolve((content.disposable === 'false' && isEmail(value)))  
    }
  });
 }
});

The mistake I made was that I tried to resolve my Promise outside of the callback_function of my request. But the data I want to use is obviously only available inside of it on time.

EDIT: for the purpose of understanding the API call. The API returns a JSON in the form of {disposable: boolean} whether the entered email-address belongs to a DEA (disposable Email Address) service

Upvotes: 1

Related Questions