user14697413
user14697413

Reputation:

How to return `res` from another function that is called in an express `router`?

I'm trying to do something like this,

// /api/auth

const validateFields = ({ name, email }, res) => {
    if (!name.trim()) return res.status(422).json({ error: 'name' })
    if (!email.trim()) return res.status(422).json({ error: 'email' })
}

router.post('/', async (req, res) => {
    const { name = '', email = '' } = req.body

    validateFields({ name, email }, res)

    return res.end()
})

If someone passes an invalid name or email, let's say an empty string, the frontend received the expected error, but the backend (ExpressJS) throws an error,

Error: Can't set headers after they are sent

I'm kind of forced to do the following,

const validateFields = ({ name, email }, res) => {
    if (!name.trim()) return res.status(422).json({ error: 'name' })
    if (!email.trim()) return res.status(422).json({ error: 'email' })

    return true
}

router.post('/', async (req, res) => {
    const { name = '', email = '' } = req.body

    const validFields = validateFields({ name, email }, res)

    if (validFields !== true) return

    return res.end()
})

Is this my only option here?

Upvotes: 1

Views: 1643

Answers (2)

Mohammad Yaser Ahmadi
Mohammad Yaser Ahmadi

Reputation: 5051

for validate req.body or req.params use 'Joi', joi is the most powerful schema description language and data validator for JavaScript, check this article

Upvotes: 0

jfriend00
jfriend00

Reputation: 707986

That isn't the only way to code it, but you will generally have to check to see if there was an error and, if not, then send your normal response.

I personally don't like functions that sometimes send a response and sometimes don't as that confuses the logic flow. I find it better to either make a function that sends all possible responses or make it a function that just returns status and the caller then sends the response. Or, use the middleware design where if it sends a response, then processing doesn't continue any further (because next() is not called).

In this particular case, you could just return the error status and let the caller send the final response. Then it's always consistent. The response is always sent by the caller and validateFields() just returns a result:

const validateFields = ({ name, email }) => {
    if (!name.trim()) return { status: 422, error: 'name' };
    if (!email.trim()) return { status: 422, error: 'email' };
    return { status: 200, ok: true };
}

router.post('/', async (req, res) => {
    const { name = '', email = '' } = req.body;
    const result = validateFields({ name, email });
    res.status(result.status).json(result);
});

The middleware design could work like this:

const validateFields = (req, res, next) => {
    const { name = '', email = '' } = req.body;
    if (!name.trim()) return res.status(422).json({ error: 'name' });
    if (!email.trim()) return res.status(422).json({ error: 'email' });
    next();
}
router.post('/', validateFields, async (req, res) => {
    res.send("ok");
});

Upvotes: 2

Related Questions