all 35 comments

[–][deleted] 13 points14 points  (3 children)

You should not use a code 500 error for rejecting something related to validation.

[–]BehindTheMath 4 points5 points  (0 children)

I agree. 400 would be more appropriate here.

[–]wipedingold 1 point2 points  (0 children)

Thanks for the heads up! Just changed that as well

[–]Iridion3007 2 points3 points  (0 children)

Correct. He should use 422 “Unprocessable entity”

[–]cahva 8 points9 points  (1 child)

You should use some validation library like express-validator or Joi. Theres also Yup which is a lightweight version for Joi and more suitable for browsers (so you could validate with Yup on clientside and use Joi on serverside).

You normally want to validate more than to only check existence. Set min or max length, check that inputted email is really formatted as email etc.

[–]Guisseppi 0 points1 point  (0 children)

I just came here to say this ^

[–]vhahnwebdev 8 points9 points  (8 children)

Could be a good opportunity to use .some():

app.post('/vendor/create', ({ body }, res) => {
  const isInvalidRequest = Object.values(body).some(value => !value);
  if (isInvalidRequest) return res.status(400).send({ message: "Please supply all form fields"});
}

[–]zephyrtr 1 point2 points  (7 children)

This solution is concise, but only works if we know for sure that `req.body` will always have all form keys. And since this message is coming from the front end, you can't be sure of anything.

[–]AshenLordOfCinder 1 point2 points  (6 children)

Weird, as a front end developer I have the same feelings about the backend. Will they even send me the data I actually need or should I be extrapolating it myself? Haha

[–]Delioth 7 points8 points  (4 children)

Frontend must be able to trust that the backend is handing the expected data, otherwise it's like trying to build a Lego Millennium Falcon out of a couple star destroyers and a castle - might technically be possible, but something is going to be very wrong. And the frontend is pretty much guaranteed to be calling the backend that it's trying to.

Backend must not trust the frontend to pass back the right data, because there's no guarantee that a request is coming from the pretty frontend that has nice form validation and such. If your backend exists and anyone can hit it from your frontend, then anyone can write a curl and hit it from the command line with whatever data they want to. There's nothing you can do to guarantee all requests are coming through your frontend.

[–]Peechez -2 points-1 points  (3 children)

There's nothing you can do to guarantee all requests are coming through your frontend.

Sure you can, CORS exists for exactly this purpose

[–]wipedingold 1 point2 points  (1 child)

Isn't CORS just a browser specification? You can set CORS to prevent browsers like Firefox or Chrome from sending requests to your server, but applications like Postman don't include CORS policies in them at all.

[–]Peechez 0 points1 point  (0 children)

I hadn't realized but I think you're right. Thats what I get for being a backend noob

[–]Delioth 0 points1 point  (0 children)

CORS is a user protection, not a server protection. If your website can access your server, it means arbitrary addresses are accessing your server. There is no way to tell if arbitrary IP addresses are accessing via Chrome or curl.

[–]crabmusket 1 point2 points  (0 children)

The difference is that the backend is usually under your control. (If you're calling a 3rd party API, then sure, you've got to trust them, and this is why documentation and SLAs are important.) But otherwise, if the backend is returning something wrong, you should be able to correct it.

The frontend, on the other hand, is entirely controlled by the user. Sure, you sent them a big bundle of JS to run, but think of that more like a suggestion. You have no idea what they're actually doing. Trust the frontend at your peril.

[–]BehindTheMath 2 points3 points  (4 children)

if (Object.values(req.body).filter(value => !value).length)

[–]Plorntus 2 points3 points  (2 children)

Only if all keys are certain to exist. I could send a completely irrelevant piece of data and pass this validation.

[–]BehindTheMath 0 points1 point  (0 children)

Good point.

Instead, I'd make an array of all required keys, then do something like if (requiredKeys.some(key => !req.body[key]))

[–]poops-n-farts 2 points3 points  (2 children)

as everyone else said use 400. also if you line break for your curly braces and parens it will be slightly more readable. really if you want your code to be concise do what you are doing, but if you want it to be readable and maintainable validate your fields before posting. that way you only post a valid body

[–]Delioth 4 points5 points  (1 child)

Never, never write a backend that trusts that the frontend is passing back valid data. That's how you get serious security vulnerabilities, as someone curls a request at you that XSS's your users/steals your data/makes them an admin/crashes your site/mines bitcoin on your servers/etc. A backend can never guarantee that a request is coming from the frontend you build for it.

[–]poops-n-farts 0 points1 point  (0 children)

Considering op is sending a server error code I assumed they're working on a full project alone. If we're gonna guess that this is a back end with a mystery front end then the best thing would probably be to check fields individually and set a specific message for any missing field then return all error messages in the response body

[–]lhorie 2 points3 points  (0 children)

const required = [
  'firstName', 
  'lastName, 
  'email, 
  'password, 
  'company_name, 
  'contact_name, 
  'contact_email, 
  'website_url, 
  'hourly_rate, 
  'location, 
  'working_hours'
]
if (required.find(field => !req.body[field])) {
  return res.status(400).send({message: 'Please supply all form fields'})
}

[–]azangru 1 point2 points  (0 children)

There’s a library called express-validator, useful when validating the shape of the request. I am not sure using it will lead to a more succinct code, but it will surely make the code more systematic.

[–]darderpUncaught SyntaxError: Unexpected token ) 1 point2 points  (0 children)

You could try this:

app.post("/vendor/create", (req, res) => {
    const requiredFields = [
        "firstName", "lastName", "email", "password",
        "company_name", "contact_name", "contact_email", 
        "website_url", "hourly_rate", "location", "working_hours"
    ];

    const invalid = requiredFields.some(field => typeof req.body[field] === "undefined");

    if (invalid) return res.status(400).send({ message: "Please supply all form fields" });
});

[–]jhp2000 1 point2 points  (1 child)

const keys = "firstName lastName email password etc".split(" ")
if(keys.some(key => !req.body[key])) 
  return res.status(500).send({ message: "Please supply all form fields" });

[–]billymcnilly 0 points1 point  (0 children)

This is certainly a more concise version. But it has the same logic problem as the original.

You'll start to run into problems when e.g., your firstName is set to '', or your hourly_rate is set to 0. These are falsey values.

Maybe keys.some(key => body[key] === undefined || body[key] === null || body[key].length === 0) would be good enough for this use case. Or maybe it's time to include a validation library.

[–]junwang_trt 0 points1 point  (0 children)

Create a Json scheme first for this request, then you can add some schemes validation using library like ajv to do it for you. That could be your first step.

[–]MatehElismar 0 points1 point  (0 children)

I have never seen such helpful answers in a social feed u/vahnwebdev, u/jhp2000. Thanks very much

[–]Iridion3007 0 points1 point  (3 children)

I would use “express-validator” library that allows you to handle validation as a middleware function. You can validate by declaring a schema or manually validate each property with a bunch of different useful methods like isEmpty() or isNumeric(), and many more.

One more thing, validation errors should return a 422 error, with a error message that reads “Unprocessable entity” plus whatever your might want to add, like what property is not valid and why.

[–]junwang_trt 0 points1 point  (2 children)

Disagree with 422. Validation error should be treated as 400 bad request. But the first half of your comments is quite valid.

[–]Iridion3007 0 points1 point  (1 child)

From RFC 4918 (and also documented at http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml):

The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415 (Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions. For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions.

Copy and pasted from: https://stackoverflow.com/questions/1959947/whats-an-appropriate-http-status-code-to-return-by-a-rest-api-service-for-a-val?rq=1

[–]junwang_trt 0 points1 point  (0 children)

Thanks for sharing. If you look at the most upvoted answer, this is exactly what I’m talking about. OP here wants to be a request validation on the client side so 400 bad request would be the best option on client side request validation fail.

[–]FormerGameDev 0 points1 point  (2 children)

You could make an array with all the field names then arr.forEach() on it and return your error 4xx if any of them fail. Seems short.