How to extract response object from express? - javascript

I am trying to build a nodejs app with mysql and what I want is
that my controllers and messages that I send should be in separate file
as shown below
this is my auth controller file
exports.signup = (req, res) => {
try {
const { name, username, email, phone_number, gender } = req.body;
const payload = [name, username, email, phone_number, gender];
connection.query(signupUserQuery, payload, (error) => {
if (error) {
return errorMessage('Signup Failed');
} else {
return createdMessage('Signup SuccessFul');
}
});
} catch (error) {
return errorMessage('Signup Failed');
}
};
and this is my messages file where I keep my messages
module.exports = {
createdMessage: function (message) {
return res.status(201).json({
isError: false,
message: message,
});
},
errorMessage: function (message) {
return res.status(500).json({
isError: true,
message: message,
});
},
};
So what I want is I want to return this function whenever controller is executed but the problem is when I try to do this I got error as res is not defined ,
So is there any way to use res in this messages.js File
What I have tried is I send res object from controller and that works but that is repetitive and I do not want to repeat myself
And one more thing I write my queries in separate file link this
insert into tbl_user (name,username,email,phone_number,gender) values (?,?,?,?,?)
but here the problem is I have to put question marks according to fields I require
so is there any way to do that in single question mark ?

You can use it like this.
This is your messages.js file.
function createdMessage (message) {
return {
isError: false,
message: message
}
}
function errorMessage (message) {
return {
isError: true,
message: message
}
}
module.export = {
createdMessage,
errorMessage
}
This is auth controller.js
const { createdMessage, errorMessage } = require("./messages.js");
exports.signup = (req, res) => {
try {
const { name, username, email, phone_number, gender } = req.body;
const payload = [name, username, email, phone_number, gender];
connection.query(signupUserQuery, payload, (error) => {
if (error) {
return res
.status(400)
.send(errorMessage('Signup Failed'));
} else {
return res
.status(201)
.send(createdMessage('Signup SuccessFul'));
}
});
} catch (error) {
return res
.status(400)
.send(errorMessage('Signup Failed'));
}
};

Related

Node Js: Remove string array element from mongoDB

I have a user schema as follows:
const UserSchema = new mongoose.Schema({
skills: [String]
});
module.exports = mongoose.model("User", UserSchema);
And a Fetch request to delete a skill as follows:
const deleteItem = async (id) => {
try {
await fetch(`http://localhost:5000/api/user/deleteskill`, {
method: "DELETE",
headers: { "Content-Type": "application/JSON", token: accessToken },
body: JSON.stringify({ userid: userid , skill:id}),
})
.then((res) => res.json())
.then((data) => {
console.log("USER SKILLS:", data.userskills);
});
} catch (err) {
console.log(err);
}
};
Server
const deleteSkill = async (req, res) => {
try {
const user = await User.findById(req.body.userid)
//user.skills.pull(req.body.skill);
// removeskill = user.skills.filter(function(item) {
// return item !== req.body.skill
// })
if (user.skills.includes(req.body.skill)) {
res.status(400).json("Item Still Exists");
} else {
res.status(200).json("Item Deleted");
}
} catch (error) {
res.status(500).send({ error: error.message });
}
};
the array is in the following structure
[
'skill1', 'java', 'skill5'
]
I have tried to remove the user skill from the array in several ways but I still get res.status(400).json("Item Still Exists");. What I'm doing wrong?
Use the findOneAndUpdate method to find a document with the user id and update it in one atomic operation:
const deleteSkill = async (req, res) => {
try {
let message = "Item Deleted";
let status = 200;
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{ $pull: { skills: req.body.skill } },
{ new: true }
)
if (user && user.skills.includes(req.body.skill)) {
message = "Item Still Exists";
status = 400;
} else if (!user) {
message = "User Not Found";
status = 404;
}
res.status(status).send({ message });
} catch (error) {
res.status(500).send({ error: error.message });
}
};
I believe you want to remove skills from the database then the following function could help you out.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("mydb");
var myquery = { userid: userid, skillid: skillid};
dbo.collection("skills").deleteOne(myquery, function(err, obj) {
if (err) throw err;
console.log("1 document deleted");
db.close();
});
});
You have a method of removing elements from arrays, if you want to remove the first one you could use array.shift (more on it here), but if you want to delete it completely from your database you could always, find it and then update it.
User.update({ _id: userid }, { $pull: { "skills": "[skill]" }})

Error in updating profile with image using mongoose and cloudinary

updateProfile: async function(req, res) {
try {
const update = req.body;
const id = req.params.id;
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
const image = req.files.profileImage;
const cloudFile = await upload(image.tempFilePath);
const profileImage = cloudFile.url
console.log('Loging cloudfile', profileImage)
await User.updateOne(id, { update }, { profileImage }, { new: true },
function(err, doc) {
if (err) {
console.log(err)
}
if (doc) {
return res.status(200).send({ sucess: true, msg: 'Profile updated successful' })
}
});
} catch (error) {
res.status(500).json({ msg: error.message });
}
}
But I'm getting an error of "Callback must be a function, got [object Object]"
I have tried to $set: update and $set: profileImage but still not working.
So the image successful upload into the cloudinary but the update for mongoose is not working.
Upon brief research into the issue, I think you are feeding the arguments in wrong. Objects can be confusing but not to worry.
Your code is:
await User.updateOne(id, { update }, { profileImage }, { new: true }
However, I believe it should be something more like:
await User.updateOne({id: id}, { profileImagine: profileImage, new: true },
The API reference annotates use of the function as:
const filter = { name: 'John Doe' };
const update = { age: 30 };
const oldDocument = await User.updateOne(filter, update);
oldDocument.n; // Number of documents matched
oldDocument.nModified; // Number of documents modified

Handling errors in Express.js in service / controller layers

I am writing an application in Express.js with a separate controller layer and a service layer. Here is my current code:
user.service.js
exports.registerUser = async function (email, password) {
const hash = await bcrypt.hash(password, 10);
const countUser = await User.countDocuments({email: email});
if(countUser > 0) {
throw ({ status: 409, code: 'USER_ALREADY_EXISTS', message: 'This e-mail address is already taken.' });
}
const user = new User({
email: email,
password: hash
});
return await user.save();
};
exports.loginUser = async function (email, password) {
const user = await User.findOne({ email: email });
const countUser = await User.countDocuments({email: email});
if(countUser === 0) {
throw ({ status: 404, code: 'USER_NOT_EXISTS', message: 'E-mail address does not exist.' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (validPassword) {
const token = jwt.sign({ email: user.email, userId: user._id }, process.env.JWT_KEY, { expiresIn: "10s" });
return {
token: token,
expiresIn: 3600,
userId: user._id
}
} else {
throw ({ status: 401, code: 'LOGIN_INVALID', message: 'Invalid authentication credentials.' });
}
};
user.controller.js
exports.userRegister = async function (req, res, next) {
try {
const user = await UserService.registerUser(req.body.email, req.body.password);
res.status(201).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
exports.userLogin = async function (req, res, next) {
try {
const user = await UserService.loginUser(req.body.email, req.body.password);
res.status(200).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
The code works, but requires some corrections. I have a problem with error handling. I want to handle only some errors. If another error has occurred, the 500 Internal Server Error will be returned.
1) Can I use "throw" object from the service layer? Is this a good practice?
2) How to avoid duplication of this code in each controller:
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
3) Does the code require other corrections? I'm just learning Node.js and I want to write the rest of the application well.
Yes, you can throw errors from service layer, it is good practice to catch errors with try/catch block in controller
I handle this with a custom error middleware, just use a next function in a catch block.
catch (e) {
next(e)
}
Example of error middleware (for more info check docs, fill free to move a middleware to file)
app.use(function (err, req, res, next) {
// err is error from next(e) function
// you can do all error processing here, logging, parsing error messages, etc...
res.status(500).send('Something broke!')
})
From my point of view it looks good. If you looking for some best practice and tools, try eslint (with AirBnb config for example) for linting, dotenv for a environment variables management, also check Node.js Best Practice
i want to give you an example:
this code in your controller
findCar(idCar)
} catch (error) {
switch (error.message) {
case ErrorConstants.ELEMENT_NOT_FOUND('LISTING'): {
return {
response: {
message: ErrorMessages.ELEMENT_NOT_FOUND_MESSAGE('LISTING'),
},
statusCode,
}
}
default: {
return {
response: {
message: ErrorMessages.UNKNOWN_ERROR_MESSAGE,
},
statusCode,
}
}
}
}
and this code in your service
findCar: async listingId => {
try {
if (some condition) {
throw new Error(ErrorConstants.ELEMENT_NOT_FOUND('LISTING'))
}
return { ... }
} catch (error) {
console.error(error.message)
throw new Error(ErrorConstants.UNKNOWN_ERROR)
}
},
controller is going to catch the service's errors

Select only records where domain based on user "permission"

Hello everyone I'd like to get some tips on how to solve my problem.
I got table where I stores user details like:
ID, FIRST_NAME, LAST_NAME EMAIL, PASSWORD, ROLE, DOMAIN
also got table named Aliases where I got
ID, DOMAIN_ID, DOMAIN, DESTINATION
In user table role means admin, moderator, user etc. and Domain for example is aaa.pl, bbb.pl. Also Aliases table got domain like aaa.pl, bbb.pl
I want to select from table only aliases where the domain is the same as assigned to the user.
So user X can sees only aliases Where is the same domain.
backend
This is my controller
public async aliasesListByDomain(req: Request, res: Response): Promise<void> {
const { domain } = req.params;
const aliasesListByDomain = await pool.query('SELECT * FROM virtual_aliases WHERE domain= ?', [domain]);
if (aliasesListByDomain.length > 0) {
res.json(aliasesListByDomain);
} else {
res.status(404).json({ message: "Alias doesn't exists" });
}
}
There is how I authenticate user
router.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
})
.then(user => {
if (user) {
let token = jwt.sign(user.dataValues, process.env.SECRET_KEY, {
expiresIn: 1440
})
res.json({ token: token })
} else {
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
And now I have problem with my frontend.
This is my Service where I get all aliases ( no matter of domain )
getAliases(): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases`);
}
and there is my component where I get all Aliases
getAliases() {
this.aliasesService.getAliases().subscribe(
res => {
this.alias = res;
console.log(this.alias);
},
err => console.error(err)
);
}
Now how can I select only aliases based on users permission.
I tried something like this:
Service
getAliasesByDomain(domain: string): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases/${domain}`);
}
This is my auth service
to login
public login(user: TokenPayload): Observable<any> {
const base = this.http.post(`http://localhost:3000/users/authenticate`, user);
const request = base.pipe(
map((data: TokenResponse) => {
console.log(data);
if (data.token) {
this.saveToken(data.token);
}
return data;
})
)
return request;
}
and UserDetails
public getUserDetails(): UserDetails {
const token = this.getToken();
let payload;
if (token) {
payload = token.split('.')[1];
payload = window.atob(payload);
return JSON.parse(payload);
} else {
return null;
}
}
Should I in my component get usertoken then JSON.parse() it and get domain
details from logged user? Then send it to api?
What is best solution for this?
-Edit
I did something like this:
private getToken(): string {
if (!this.token) {
this.token = localStorage.getItem('usertoken');
}
return this.token;
}
getAliasesByDomain(){
const token = this.getToken();
let user;
if (token) {
user = token.split('.')[1];
user = window.atob(user);
user = JSON.parse(user);
console.log('user z from getAliasesByDomain: '+user.domain);
this.aliasesService.getAliasesByDomain(user.domain).subscribe(
res => {
console.log(res);
this.alias = res;
},
err => console.error(err)
);
}
}

Post data to mongodb

Hi i have end point to post data to mongodb , when i submit a form only ID is submitted I think because am using insert instead of save ,
Here is how it looks:
app.post('/comments', (req, res) => {
const { errors, isVal } = validate(req.body);
if (isVal){
const { author, description } = req.body;
db.collection('comments').insert({ author, description }, (error, result) => {
if (error) {
res.status(500).json({ errors: { global: "Oops something is right!" }});
} else {
res.json({ comments: result.ops[0] });
}
})
} else {
res.status(400).json({ errors });
}
});
The method above is the one saves only ID, other data saved null: I tried to change like this, replacing insert with save some one suggested something like this.
app.post('/comments', (req, res) => {
const { errors, isVal } = validate(req.body);
if (isVal){
const { author, description } = req.body;
db.collection('comments').save({ author, description }, (error, result) => {
if (error) {
res.status(500).json({ errors: { global: "Oops something is right!" }});
} else {
res.json({ comments: result.ops[0] });
}
})
} else {
res.status(400).json({ errors });
}
});
Still the same : here is the result saved in database:
{
"_id": {
"$oid": "5b281457f5b629565c09ce26"
},
"author": null,
"description": null
}
how can I change my method so that it can use save instead of insert?
and what is the different between save and insert in mongodb?
Try with this
let newcollection = db.collection('comments');
newcollection.insert({})

Categories

Resources