0

This is how I've designed my route and I'm not really happy with it. I'd rather be able to break down each hasValid into its own middleware, but I don't see how that would work since it wouldn't stop execution.

const secretSender = async (req, res, next) => {
  if (!hasValidA(req)) {
    return next()
  }

  if (!hasValidB(req)) {
    return next()
  }

  if (!hasValidC(req)) {
    return next()
  }

  if (!hasValidD(req)) {
    return next()
  }

  res.send('something secret')
}

router.use(secretSender)
router.get('*', (req, res) => {
  res.send('something public')
})

It also doesn't really make sense to make the default route "something secret" and have "something public" be the middleware since "something public" is the default behavior.

Dave
  • 457
  • 1
  • 7
  • 16
  • `const checks = [hasValidA, hasValidB, hasValidC, hasValidD]; if (checks.some(c => !c(req))) { return next(); }` – zerkms Mar 20 '19 at 03:46
  • why dont you join all your conditions with OR like `if (!hasValidA(req) || !hasValidB(req) ) {return next()}` – Tushar Gupta Mar 20 '19 at 03:47
  • _"but I don't see how that would work since it wouldn't stop execution"_... You _can_ stop the `request-response` cycle from a middleware by not calling the `next()` function [[doc](https://expressjs.com/en/guide/using-middleware.html)], see here for example: https://stackoverflow.com/a/33649566/3165255 – IronGeek Mar 20 '19 at 04:39
  • @IronGeek if I don't call next then it times out and never sees my "something public" route – Dave Mar 20 '19 at 04:42
  • @Dave I'm confused, you wanted the middleware to short-circuit the execution or not? not calling `next()` means you're stopping the execution chain, the request however will still be left hanging. Therefore —and I quote from [the attached answer above](https://stackoverflow.com/a/33649566/3165255)— "so if a middleware needs to end the request-response early, simply do not call `next()` but make sure that the middleware really ends the request-response by calling `res.end`, `res.send`, `res.render` or any method that implicitely calls `res.end`" – IronGeek Mar 20 '19 at 04:53
  • @Dave this: _I'd rather be able to break down each hasValid into its own middleware, but I don't see how that would work since it wouldn't stop execution_ – IronGeek Mar 20 '19 at 05:24

1 Answers1

1

Here's one way to break down each hasValid into its own middleware. Each middleware will short-circuit the execution if the outcome is valid:

const validA = async (req, res, next) => {
  if (hasValidA(req)) {
    res.send('something secret') 
  } else {
    next()
  }
}

const validB = async (req, res, next) => {
  if (hasValidB(req)) {
    res.send('something secret')
  } else {
    next()
  }
}

const validC = async (req, res, next) => {
  if (hasValidC(req)) {
    res.send('something secret') 
  } else {
    next()
  }
}

const validD = async (req, res, next) => {
  if (hasValidD(req)) {
    res.send('something secret')
  } else {
    next()
  }
}

router.use(validA, validB, validC, validD)
router.get('*', (req, res) => {
  res.send('something public')
})

Update

To achieve somewhat similiar result with OP's code, but only with single next():

const secretSender = async (req, res, next) => {
  if (hasValidA(req) && hasValidB(req) && hasValidC(req) && hasValidD(req)) {
    res.send('something secret')
  } else {
    next()
  }
}

router.use(secretSender)
router.get('*', (req, res) => {
  res.send('something public')
})
IronGeek
  • 4,764
  • 25
  • 27
  • this sends the secret if it meets it single requirement, not what my code does – Dave Mar 20 '19 at 05:47
  • To be fair, `hasValid*` here could be anything, a single requirement or even multiple ones depends on how you define it. And each `middleware` here defines it's own _set of requirement(s)_... but i guess this's not what you after... – IronGeek Mar 20 '19 at 06:30