1

Apologies for the confusing title, I will attempt to explain

I am using node.js with express-form and the custom validation function like so:

app.post('/register', form(
    field("username").trim().required().custom(function(value) {
       //Works correctly
       if (!user.validUsername(value)) {
           throw new error("Invalid username");
       }

       //Does not work due to callback
       user.uniqueUsername(value, function(uniqueUsername) {
            if (!uniqueUsername) {
                //Not unique, throw error
                throw new Error("username is already taken");
                //Could be subsituted for return "whatever"; and it would still be the same problem
            }
        });
    }),
    registerAttempt);
};

// ...
//Example function for brevity  
user.uniqueUsername = function(username, callback) {
    User.findOne({username: username}, function(err, user) {
        if (err) { throw err; }
        callback(user === null);
    });
}

I need a way to restructure this so the .custom(function(value) { .. }) doesn't finish executing until I have received the callback but I have no idea how I could do it, if at all.

EDIT: corrected error

2 Answers 2

2

It looks like express-form doesn't support asynchronous validations, so the only way to that is implement custom middleware. It can look like this:

app.post('/register',
    function (req, res, next) { //here it goes
        user.uniqueUsername(req.body.username, function(uniqueUsername) {
            req.body.uniqueUsername = uniqueUsername; //we can't do anything about it yet
            next();
        });    
    },

   form(field('username').trim().required().custom(function(value) {
        if (!user.validUsername(value)) {
             throw new error("Invalid username");
        }   
   }),
   field('uniqueUsername').custom(function(value) {
        if (!value) {
             throw new Error("username is already taken");
        }   
   }),

   registerAttempt);

It can be used as generic workaround for express-form being unable to make async validations: we pull all the data we need in a separate middleware and inject it as new fields to be then validated by express-form.

Sign up to request clarification or add additional context in comments.

Comments

-1

For situations like this, you need to invert the order of call. Something like:

User.findOne({username: username}, function(err, uniqueUsername) {
    if (err) { throw err; }

    app.post('/register', form(
        field("username").trim().required().custom(function(value) {
            if (!user.validUsername(value)) {
                throw new error("Invalid username");
            }
            if (!(uniqueUsername === null)) {
                //Not unique, throw error
                throw new Error("username is already taken");
            }
        }),
        registerAttempt
    );
};

Basically, you need to fetch the values before using them (when stated like that it seems obvious but async programming tend to make the obvious not so obvious).

If you need to check multiple async items then you need to chain them together somehow. The async library can help with that if necessary.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.