I'm new to express + node.js, so I'm written an rest api using mongoose. What's the best way to handle runtime errors as database errors etc.?
I've read in the express documentation that you can have a middleware function(err, res, req, next) to handle this errors, and you can call this function only calling next(err). That's ok, so Imagine you have a User moongose model and in a controller you write this function:
const find = (email, password) => {
User.find({ email: email }, (err, doc) => {
if (err) {
// handle error
}
return doc;
});
};
Then, you have in another file a handler for a route:
router.get('/users', (req, res) => {
userController.find(req.body.email);
});
So, at this point, you can handle the mongo error writing throw(err) in the model and using try/catch in the controller to then call next(err) right? But I've read that using try/catch in JavaScript is not a good practice because it creates a new execution context etc.
What's the best way to handle this errors in Express?
I will suggest you to use promises. It not only makes your code cleaner but also error handling is much easier. For reference you can visit this or this.
If you are using mongoose you can plugin your own library of promise.
const mongoose = require('mongoose');
mongoose.connect(uri);
// plug in the promise library:
mongoose.Promise = global.Promise;
mongoose.connection.on('error', (err) => {
console.error(`Mongoose connection error: ${err}`)
process.exit(1)
})
And use it like below:
In controller:
const find = (email) => {
var userQuery = User.find({ email: email });
return userQuery.exec();
};
In Router:
router.get('/users', (req, res) => {
userController.find(req.body.email).then(function(docs){
// Send your response
}).then(null, function(err){
//Handle Error
});
});
Related
everyone I have such a question, I fetch data in node js from MongoDB, also I Have two routes where was fetched "users" and "posts", check the code:
// Mongoose schema models
const users = require('../models/users')
const posts = require('../models/posts')
routes.get('/users', (req, res) => {
users.find({}, (err, users) => {
res.end(users)
})
})
routes.get('/posts', (req, res) => {
posts.find({}, (err, posts) => {
res.end(posts)
})
})
What do you think? it is right? I know it works, but I want to know if it's good or if I can do better. also, consider that I get those data in react js and then I filter as I want and where I want. in addition, I want to get advice about redux, what do you think? it's necessary to use it when we get data? Share your opinion
thank's
Reading from a DB is a async operation and you'll have to await the answer.
router.get('/users', async (req, res) => {
const users = await User.find().sort('name');
res.send(users);
})
This is pretty much it if you just want to fetch all the data and return it to the frontend.
However, I would suggest one addition to your query, and that is adding the .lean() to the query since it will increase the performance. By default, Mongoose will hydrate the data before returning it, so when you add .lean(), it will tell Mongoose that it should skip the hydration of the data and return the pure JavaScript objects.
Also, you should have error handling in case of an error.
// Mongoose schema models
const Users = require('../models/users')
const Posts = require('../models/posts')
routes.get('/users', async (req, res) => {
try {
const users = await Users.find().lean();
return res.status(200).json(users);
} catch (error) {
return res.status(400).json({ success: false });
}
})
routes.get('/posts', async (req, res) => {
try {
const posts = await Posts.find().lean();
return res.status(200).json(posts);
} catch (error) {
return res.status(400).json({ success: false });
}
})
I am having trouble understanding the use of await in Node with mongoose and express.
Basically I have a page /profile where I generate a token for every get request I have, according to the user that is connected:
router.get('/profile', requiresLogin, (req, res, next) => {
User.findById(req.session.userId).exec(function(err, user){
if(err || !user){
//Display error and return
return res.send("Error");
}
var token = Token.generateToken(user);
console.log("Token is: " + token);
return res.send("Working");
}
})
The function to generate the token is the following:
TokenSchema.statics.generateToken = function(user){
const newToken = new this({
_userId: user._id,
token: crypto.randomBytes(16).toString('hex')
});
await newToken.save();
return newToken;
}
And I get an error on the await line saying: SyntaxError: await is only valid in async functions and the top level bodies of modules.
I think the code is quite intuitive, so what I want to accomplish is to have an static function to generate the Token objects. Then this function will return and other methods (like the one I posted that gets executed when getting /profile) can use it to create new tokens.
Is this the proper way to achieve what I am trying to achieve? I would like recommendations on how to do this code more "nodejs", since I am used to other languages where there are no promises and so on, and this is the best I could come up with. I know it is possible to do with callbacks and so on, but I think they might complicate the code for what it should be with other languages.
Thanks!
UPDATE: just after posting I saw that removing the await does what I expected, but as I have seen in here:
The save() method is asynchronous, so it returns a promise that you can await on.
So I don't really understand how nor why it is working
You can't use await in a non-async funciton, which means that:
generateToken must be async.
When calling generateToken in the request handler, you must also await that call.
Again, you can't use await there without marking the anonymous handler function as async.
Your handler should look like this:
router.get("/profile", requiresLogin, (req, res, next) => {
User.findById(req.session.userId).exec(async (err, user) => {
if(err || !user) {
return res.send("Error");
}
const token = await Token.generateToken(user);
console.log("Token is: " + token);
return res.send("Working");
});
});
And the token generation function:
TokenSchema.statics.generateToken = async function(user) {
const newToken = new this({
_userId: user._id,
token: crypto.randomBytes(16).toString('hex')
});
await newToken.save();
return newToken;
}
You can also use async/await with Mongoose (instead of using callbacks), so your handler could look like this:
router.get("/profile", requiresLogin, async (req, res, next) => {
try {
const user = await User.findById(req.session.userId).exec();
if (!user) return res.send("Can't find user.");
const token = await Token.generateToken(user);
console.log("Token is: " + token);
return res.send("Working");
} catch (err) {
return res.send("Error.");
}
});
I'm trying to learn mongoose with nodejs and created a following node function as below where in I'm receiving the email ID from the Angular application and based on the EmailId trying to find the records in MongoDb:
const express = require('express');
const mongoose = require('mongoose');
var bodyParser= require("body-parser");
const port = 3000;
const app = express();
//setting up bodyParser middleWare
app.use(bodyParser.urlencoded({"extended":true}))
app.use(bodyParser.json());
//setting mongoose connection to local mongoDb
mongoose.connect("mongodb://127.0.0.1/userDetails",{useNewUrlParser:true,useUnifiedTopology:true})
const db = mongoose.connection;
var userSchema = new mongoose.Schema({
email:{type:String},
userId:{ type: String, required: true },
password:{ type: String, required: true }
});
var users = mongoose.model('User', userSchema);
//error handler middleWare
app.use(function(err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
app.post("/login",async(req,res,next)=>{
try {
await users.find({ email:"xyz#gmail.com"}).exec(function (err, docs) {
if (err){
console.log(err);
}
else{
console.log("First function call : ", docs);
}
})
}
catch(err){
next(err);
}
})
app.listen(port,()=>{console.log("server started")});
The above code shows no error, but the output is always First function call : [].My mongodb collection is as follows
db.User.insertMany([
{ email: "xyz#gmail.com", userId:1,password : "123#124" },
{ email: "abc#yahoo.com",userId:2,password : "123#125"},
{ email: "lmn#outlook.com", userId:3,password : "123#126"}
])
db.User.find({ email: "xyz#gmail.com"});
Where is that I'm going wrong ,what should be the approach to get the data?
Your code should find and print the data to the console assuming the email address you're looking for is actually in the collection. However, I want to point out a few things that don't quite make sense.
There is absolutely no reason to await the function call await users.find({ email:"xyz#gmail.com"}).... So you should change it to users.find({ email:"xyz#gmail.com"})
With that change, there is absolutely no reason for the router function to be async. So this is how it should look app.post("/login", (req, res, next) => {...
You're not sending a response back to the caller of /login. So when you test, it just hangs there until you cancel your request. You should add res.json({msg: 'ok'}) below console.log("First function call : ", docs);
And finally, mongoose is an awesome, promise-based library. Why not use its promise capability? It will greatly clean up your code. Here is how I would re-write your code:
app.post("/login", (req, res, next) => {
users.find({email:"xyz#gmail.com"})
.then(docs => {
console.log(docs);
res.json(docs);
})
.catch(e => {
console.log(e);
next(e);
})
})
I would like some basic error handling on every route, so if there is ever an exception, the API at least responds with 500.
According to this pattern, you still need to include a try/catch block in every route:
app.post('/post', async (req, res, next) => {
const { title, author } = req.body;
try {
if (!title || !author) {
throw new BadRequest('Missing required fields: title or author');
}
const post = await db.post.insert({ title, author });
res.json(post);
} catch (err) {
next(err) // passed to the error-handling middleware
}
});
That seems a little repetitive. Is there a higher-level way where exceptions are automatically caught everywhere and passed to the middleware?
I mean, it would obviously be possible for me to just define my own appGet():
function appGet(route, cb) {
app.get(route, async (req, res, next) => {
try {
await cb(req, res, next);
} catch (e) {
next(e);
}
});
}
Is there some built in version of this?
You can use express-promise-router package.
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
E.g.
app.ts:
import express from 'express';
import Router from 'express-promise-router';
import bodyParser from 'body-parser';
const router = Router();
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.use(router);
router.post('/post', async (req, res) => {
const { title, author } = req.body;
if (!title || !author) {
throw new Error('Missing required fields: title or author');
}
const post = { title, author };
res.json(post);
});
router.use((err, req, res, next) => {
res.status(500).send(err.message);
});
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
You don't need try/catch statement block anymore.
Test result:
I think the better approach would be to divide the services and the controllers which is demonstrated below.
Add post service:
async function addPostService (title, author) => {
if (!title || !author)
throw new BadRequest('Missing required fields: title or author');
return await db.post.insert({ title, author });
};
Add post controller:
function addPost(req, res, next){
const { title, author }= req.body;
addPostService
.then((post) => {
res.json(post);
})
.catch(next) // will go through global error handler middleware
}
Now, we can make a global error handler middleware which will catch the error thrown by any controller throughout the app.
function globalErrorHandler(err, req, res, next){
switch(true){
case typeof err === 'string':
// works for any errors thrown directly
// eg: throw 'Some error occured!';
return res.status(404).json({ message: 'Error: Not found!'});
// our custom error
case err.name = 'BadRequest':
return res.status(400).json({ message: 'Missing required fields: title or author!'})
default:
return res.status(500).json({ message: err.message });
}
}
And, don't forget to use the error handler middleware right before starting the server.
// ....
app.use(globalErrorHandler);
app.listen(port, () => { console.log('Server started...')});
I am cleaning my code and moving from callback hell to async/await and try/catch but I still want to make my code DRY as I have too many routes and performing same try catch in every request. What could be the best way to handle this?
this my example code in one of the GET route.
router.get('/customer', async (req, res, next) => {
try {
const customer = await Customer.find({}).populate('buisness').exec();
return res.status(200).json({
result: customer
});
} catch (e) {
return next(e);
}
});
now if I repeat same thing on every route it is not following DRY code. what could be the best?
const errorHandlerMiddleware = async (req, res, next) => {
try {
await next();
} catch (err) {
// handle the error here
}
};
router.use(errorHandlerMiddleware);
router.get('/customer', async (req, res, next) => {
const customer = await Customer.find({}).populate('buisness').exec();
return res.status(200).json({
result: customer
});
});
use errorHandlerMiddleware before all your routes, this middleware will catch every exception that is thrown from your routes.
now every time there is an exception in your route it will be caught in the middleware