Why express validator oneOf is not working as expected - node.js

I'm building a project in NodeJS and I'm also using Express. To validate my requests from the client I'm using Express validator and the methods check, oneOf, body, and etc.
I built my PUT endpoint and in parallel also I implemented a validation for the fields to update using oneOf. My goal is that when a client tries to update should at least change one of the fields otherwise a message should come ups informing the client nothing was updated.
My issue is that the PUT updates ignoring my check I implemented and cannot understand the issue as I already did similar before and was working perfectly.
My validator for update looks like:
const updateBookRules = [
oneOf([
check("title")
.exists()
.withMessage("Title is required"),
check("category")
.exists()
.withMessage("Category is required"),
check("price")
.isNumeric()
.withMessage("Price should be a number"),
check("img")
.exists()
.withMessage("Img is required"),
sanitizeBody("price").toFloat()
])
];
In the route, I'm calling this validation as:
// PUT Update a book
router.put("/:asin", check.updateBook, check.rules, async (req, res) => {
// Request ID
const asin = req.params.asin;
// Await the book
await book
// Call model to update the product
.updateBook(asin, req.body)
// Response a message
.then(book =>
res.json({
message: `The book #${asin} has been updated`,
content: book
})
)
// Errors if any
.catch(err => {
if (err.status) {
res.status(err.status).json({ message: err.message });
}
res.status(500).json({ message: err.message });
});
});
The strange is the validation is ignored and I can put also empty strings and no error happens I don't know what is my mistake.
Please comment if you would like to see something more and I will update the question.

Edited Answer
I think I may know the issue. Your code seems fine now that I've had access to the whole repo, but you definitely need to be bringing in validationResult in order to check for validation errors, then stop execution of the router middleware. So, the router.put route-handler would start with:
const { validationResult } = require('express-validator');
// PUT Update a book
router.put("/:asin", check.updateBook, check.rules, async (req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
I'm still not sure about how you setup updateBookRules to use the sanitizeBody() call, and how you have it wrapped in an array. If the above still doesn't work after adding validationResult, try this:
const updateBookRules = oneOf([
check("title")
.exists()
.withMessage("Title is required"),
check("category")
.exists()
.withMessage("Category is required"),
check("price")
.isNumeric()
.withMessage("Price should be a number"),
check("img")
.exists()
.withMessage("Img is required"),
]);
Then make the route-handler signature:
router.put("/:asin", sanitizeBody("price").toFloat(), check.updateBook, check.rules, async (req, res) => {
Also, are you sure there is an error? The docs state that oneOf :
Creates a middleware instance that will ensure at least one of the
given chains passes the validation.
You are checking to make sure that these different fields exist at the PUT route. As long as one of them does exist, check.updateBook will pass. You aren't checking for change here, so the user can simply send you back the same book information, and nothing will fail right?
Per your comments, if you really don't want to use the validationResult you can try to run the validations imperatively, check out the docs

Related

problem on creating new document in mongoose

i want to add a new document with mongoose
but it just creates only the _id without all the details I entered
in the postman i entered:
{
"name":"jhon",
"email":"jhon11#gmail",
"password":"1234"
}
but got this in the res:
{
"_id": "6072e8d3f0f69037cc05b8cb",
"__v": 0
}
my code:
const addUser = async (req, res) => {
try {
let user = new User(req.body)
await user.save()
res.send(user)
}
catch (error) {
res.status(400).send(`error ${error}`)
}
}
and the schema is:
const userSchema=mongoose.Schema({
name:{
Type:String
},
email:{
Type:String
},
password:{
Type:String,
}
})
thanks for any advice.
Postman isn't very straight forward in explaining how it sends out post requests. I'm saying this because I also had some run-downs with it. It is most likely sending out an empty body in the request. Open up postman again, and go to the post request you created. We already know your request reached the backend server, so it means the url is fine, and that its type is in fact a post request. So now, click on the Body tab.
I'm gonna go ahead and guess you chose the raw option amongst the million available options there, correct?
In the textarea zone, insert your raw json again, as you did before:
{
"name":"jhon",
"email":"jhon11#gmail",
"password":"1234"
}
But wait! before sending that out, look to the right. There's a combo-box there. A dropdown. A select menu (whatever you wanna call it).
By default, postman chooses TEXT as the default option. Change it to JSON!
NOW IT'S GONNA WORK!
Also, maybe before sending that out for test spin, i'd recommend putting a console.log in bewtween your lines and see what prints out, like so:
const addUser = async (req, res) => {
try {
let user = new User(req.body)
console.log('req.body is:', req.body)
await user.save()
res.send(user)
}
catch (error) {
res.status(400).send(`error ${error}`)
}
If your req.body is still being presented as empty, then my next question to you would be: Are you using bodyParser on your server? Because could also be the case. Body parser, as its name suggests, is meant for dealing with req.body, so it could actually be readable. try adding a body parser to your express server.
Good luck!
I hope that helps.

How to show custom express validator error message

I have used one controller and one model. After post form , i am checking either email id already exist or not. For checking email using express custom validator in controller. Inside controller call model function in which check email using mongodb. But every time condition false. Please guide me , i am new in node js.
enter image description here
Looks like the express-validator already show this as a use case of custom validators:
https://express-validator.github.io/docs/custom-validators-sanitizers.html
const { body } = require('express-validator/check');
app.post('/user', body('email').custom(value => {
return User.findUserByEmail(value).then(user => {
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), (req, res) => {
// Handle the request
});

Setting a test header in GraphQL Playground that I want to use causes "Server cannot be reached"

So I've created a bunch of mutations and queries and stitched them together that works and wanted to introduce authentication into the mix. I added an HTTP Header "x-token" to hold my sign-in token to be able to delete things like their job or the user itself.
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, "notSoSecret");
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
const server = new ApolloServer({
typeDefs: schema,
resolvers,
formatError: error => {
// remove the internal sequelize error message
// leave only the important validation error
const message = error.message
.replace('SequelizeValidationError: ', '')
.replace('Validation error: ', '');
return {
...error,
message,
};
},
context: async ({ req }) => {
const me = await getMe(req);
return {
models,
me,
secret: "notSoSecret",
}
},
path: "/graphql"
});
server.applyMiddleware({ app });
sequelize.sync().then(async () => {
createUsersWithJob();
});
app.get("/playground", graphiql({ endpoint: "/graphql" }));
const handler = serverless(app);
export { handler };
const createUsersWithJob = ... //creates seed data
So when I add the token and I look into my command line console, I actually see that I'm setting the header that I want, but it loops over and over again and doesn't stop. Also playground gets an error "Server cannot be reached"
{
"error": "Response not successful: Received status code 400"
}
and running a deleteUser mutation does not work, or any other mutation and query for that matter until I remove the HTTP Header that I set on playground.
There is a secondary issue where everything in this root file runs twice, but that's not as big for me at the moment as the header issue outlined.
If anyone has any insight into this, I'd love to know more. Thanks in advance.
edit: just a quick edit to say that it works fine when I hardcode a pre-existing user.
I had quite a struggle to get the React version of GraphQL Playground working within a very simple html setup, but I figured something out that might help you as well (fingers crossed).
I added a headers section to the config in the GraphQLPlayground.init call, like so:
const root = document.getElementById('root');
GraphQLPlayground.init(root, {
endpoint: "/graphql",
headers: {
"Authorization": "Bearer " + token
}
})
I have an element with an id root since this is embedded in HTML.
Not sure this will help you though, as I just noticed from your code sample you're calling graphiql which is a different GraphQL client than GraphQL Playground..
GraphIQL: https://github.com/skevy/graphiql-app
GraphQL Playground: https://github.com/prisma/graphql-playground

Migrating Node JS code to Apollo server

I am setting up Apollo Server on my Node app and wondered about moving the functionality over to Apollo.
I have business logic like this:
router.post(
'/login',
(req, res, next) => {
if (!req.body.email || !req.body.password) {
return 'You must send the username and the password.';
}
Users.findOne({ email: req.body.email })
.then(user => {
bcrypt.compare(req.body.password, user.password, (err, success) => {
req.user = user;
next();
});
})
},
auth.createToken,
auth.createRefreshToken,
auth.logUserActivity,
(req, res) => {
res.status(201).send({
success: true,
authToken: req.authToken,
refreshToken: req.refreshToken
});
}
);
It follows Node router architecture where I add the found user object to req object, which passes the user to the next functions - createToken etc.. using the next() function. This was ok for my server before trying to introduce GraphQL/Apollo, but now I want all this logic to be easily accessible to the Apollo resolvers.
I often hear that people are having an easy time turning their server from REST/non-GraphQL into a GraphQL server, but at the moment it's looking like it's going to be a bit of a job to go through all the logic and separate everything in to their own functions which take parameters directly rather than using the req object.
Is this a correct assumption? Or am I missing something?
Thanks!
Migrating the code you have shown above would be a very easy task. Once you build your graphql server and create your schema, etc. Then all you need to do is create login mutation. Then your resolver would handle the logic you have shown above. Then, instead of pulling the values from from req.body they would be function parameters.
A good pattern I am currently following is creating a login method on the model itself. Then the resolver calls the method on the schema (Here is an example of a project I'm doing it on now: Login method. Then here is an example of what the resolver looks like: Resolver
Hopefully that helped!

Firebase on Express - Can't return response to client with .catch(error)

I've searched for hours and could not find a single relevant thing about this.
I am using firebase on my express web server for user (admin) authentification. I basically have a separate control panel for the admins separate from the mobile app so we can control the data from the users.
I currently have this post address that does the following:
app.post('/createNewAdminAccount', function(req, res){
const password = bcryptPassword(bcryptDetails, req.body.password);
const username = req.body.username;
const auth = firebase.auth();
let userExists = false;
function userExistsResponse(){
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ "userExists": true }));
}
const promise = auth.createUserWithEmailAndPassword(username, String(password));
promise.catch(function(error){
console.log(error.message);
userExistsResponse();
});
});
The way I have userExistsResponse(); isn't exactly how I have it, but for the sake of clarity, it's like that.
It seems that the .catch() function can only accept console.log(). I even looked at this which suggests stacking a .then(), error(error) but that doesn't work no matter how I write it, and using catch(error) doesn't seem to work either.
The goal is to see that the user is authenticated and respond to the client with a message as such. I've tried setting a variable to true (as you can see by the leftover let userExists = false variable), calling a function, doing a .then() statement without a .catch() (causes the server to break with an error), etc. None of it works, and after looking through YouTube videos, Firebase documentation, and StackOverflow, I have not found a useful answer.
FIXED Credit: 0x17
app.post('/createNewAdminAccount', authenticated, function(req, res){
const password = String(bcryptPassword(bcryptDetails, req.body.password));
const username = req.body.username;
admin.auth().getUserByEmail(username)
.then(function(userRecord) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'userExists': true }));
console.log('User Exists');
})
.catch(function(error) {
const promise = auth.createUserWithEmailAndPassword(username, password);
promise.catch(function(error){
console.log(error.message);
});
console.log('User Created');
});
});
From the way i see it, for the createNewAdminAccount route, Why not you do a check for the user in the first place using admin.auth().getUserByEmail(); analyse it's response code auth/email-already-exists or a combination of some listed in this link and then proceed with admin.auth().createUser() ?
Also if your goal is to just authenticate the user, should you not be using the below mentioned method and analyze the error code for the same ?
firebase.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});

Resources