I'm using a callback to set some ip's on redis db async.
I'm trying to catch the error and send it through express to my error handler middleware.
I'm generating an error on purpose on the select method, but it doesn't catch my error.
See following code:
module.exports = (req, res, next) => {
const redis = require('redis')
const client = redis.createClient()
try {
client.select('2d', (err) => { // instead of 2 number, i use '2d' string, to generate an error on purpose
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
client.set(ip, true, 'EX', 120, (err, rep) => {
return next()
})
})
} catch (err) {
err.type = 'SilentSystem'
next(err)
}
}
From the documentation of the redis npm package, it's clear that it uses standard Node-style callbacks. In a standard Node-style callback, the first argument passed to the callback you provide is either an error or null; that's where and how errors are reported. (You've even defined a parameter called err in your code.) They can't be caught by a try/catch because control has already passed out of the try/catch (and in fact out of the function it's in) long before the error occurs.
So you'd handle it like this:
module.exports = (req, res, next) => {
const redis = require('redis')
const client = redis.createClient()
client.select('2d', (err) => { // instead of 2 number, i use '2d' string, to generate an error on purpose
if (err) {
// Handle error here
err.type = 'SilentSystem'
next(err)
} else {
// Handle success here
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
client.set(ip, true, 'EX', 120, (err, rep) => {
if (err) {
err.type = 'SilentSystem'
next(err)
} else {
next()
}
})
}
})
}
In a comment you've said:
My actual code is a bit more complex so I was trying to avoid calling to avoid repeating calling if(err) and next(err) by using try. What's a better way (less verbose) to handle errors here?
Unfortunately, that's the nature of Node-style callbacks. One option is to give yourself a filtering function you pass all those results through so your common error-handling code is there.
But: You might consider using a lib that "promisifies" Node-style callbacks so you can use promises instead, complete with their chaining mechanism which makes centralized error handling possible. (One such package is promisify, but there are others.) With "promisified" versions of client.select, client.set., etc., that code could look like this:
module.exports = (req, res, next) => {
const redis = require('redis')
const client = makeNiftyPromiseVersionOf(redis.createClient())
client.select('2d')
.then(data => {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
return client.set(ip, true, 'EX', 120)
})
.then(() => {
next()
})
.catch(err => {
err.type = 'SilentSystem'
next(err)
})
}
Note how the error handling is consolidated at the end; if there's an error in client.select, the then callback is skipped and control passes to the catch. If not, the then callback is executed and client.set is performed, and any errors from it will also go to that catch.
This also opens the door to using ES2017's async/await to write asynchronous code in a synchronous style:
module.exports = (req, res, next) => {
(async () => {
const redis = require('redis')
const client = makeNiftyPromiseVersionOf(redis.createClient())
try {
const data = await client.select('2d');
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
await client.set(ip, true, 'EX', 120)
next()
} catch (err) {
err.type = 'SilentSystem'
next(err)
}
})();
}
Side note: I would remove the require call out of the exported function, and instead do it at the module level:
const redis = require('redis')
module.exports = {
// ...
}
Related
I am trying to get the results of my simple SELECT command to the index.js file, where I would like to have all records separated in a array. If I print the results in the database.js the JSON.parse just work fine. But if I want to return them and get them into the index.js where I need them, I always get undefined when I print it.
index.js CODE
const express = require('express');
const app = express();
const database = require('./database');
app.use(express.json());
app.use(express.urlencoded());
app.use(express.static('public'));
app.get('/form', (req,res) =>{
res.sendFile(__dirname + '/public/index.html' );
console.log(req.url);
console.log(req.path);
})
app.listen(4000, () =>{
console.log("Server listening on port 4000");
database.connection;
database.connected();
//console.log(database.select());
let results = [];
//results.push(database.select('username, password'));
let allPlayer = database.select('username');
console.log(allPlayer);
});
database.js CODE
let mysql = require('mysql');
const connection = mysql.createConnection({
host: 'localhost',
database: 'minigames',
user: 'root',
password: 'root'
});
function connected(){
connection.connect((err) => {
if(err) throw err;
console.log("Connected...");
})
}
function select(attribute){
let allPlayer = [];
let sql = `SELECT ${attribute} FROM player`;
let query = connection.query(sql, (err, result, field) => {
if(err) throw err;
return Object.values(JSON.parse(JSON.stringify(result)));
})
}
module.exports = {connection, connected, select};
Understand that one of the main things that make JavaScript different from other languages is that it's asynchronous, in simple terms meaning code doesn't "wait" for the code before it to finish executing. Because of this, when you're trying to query a database, which takes some time, the code after it gets impatient and executes regardless of how to the query is doing. To solve this problem, the mysql package utilizes callbacks, which allows you to pass a function to it to execute once the query is finished with the queries result.
Because the library operates on callbacks, it doesn't return anything; that seems quite problematic for using it somewhere else, doesn't it?
To solve this problem, we can make our own callback. Or better yet, use the newer JavaScript feature called promises, where you can basically "return" anything from a function, even when you're in a callback.
Let's implement it with the query:
function select(attribute) {
return new Promise((resolve, reject) => {
let sql = `SELECT ${attribute} FROM player`;
let query = connection.query(sql, (err, result, field) => {
if(err) return reject(err);
resolve(Object.values(JSON.parse(JSON.stringify(result))));
});
});
}
To "return" from a promise, we pass a value to the resolve function. To throw an error, we call the reject function with the error as the argument.
Our new function is rather easy to use.
select("abcd").then(result => {
console.log("Result received:", result);
}).catch(err => {
console.error("Oops...", err);
});
You might look at this code and go, "Wait a minute, we're still using callbacks. This doesn't solve my problem!"
Introducing async/await, a feature to let you work just with that. We can call the function instead like this:
// all 'await's must be wrapped in an 'async' function
async function query() {
const result = await select("abcd"); // woah, this new await keyword makes life so much easier!
console.log("Result received:", result);
}
query(); // yay!!
To implement error catching, you can wrap you stuff inside a try {...} catch {...} block.
So when running a query using Objection.js, the query will return data based on success or failure of said query and this data is passed to the then() block as a 0 or 1. Meaning to error handle, I'm having to check falsey values rather than send a response in the catch block. Am I doing something wrong?
const editIndustry = async (req, res, next) => {
const industry = await Industry.query().findById(req.params.industryId);
if (!industry) {
return res.status(404).json({
error: 'NotFoundError',
message: `industry not found`,
});
}
await industry
.$query()
.patch({ ...req.body })
.then(result => console.log(result, 'then() block'))
// never runs
.catch(err => {
console.log(err);
next(err);
});
};
App is listening on port 3000.
1 then() block ran
Your code is working as expected. The reason it's not going into the catch block is because there isn't an error. patch does not return the row. It returns the number of rows changed (see docs).
The function I think you're really looking for is patchAndFetchById (see docs). If you're concerned about generating a 404 error, you can append throwIfNotFound. Obviously, this will throw if it's not found in the database, which will let you catch. You can catch an instance of this error so you can send a proper 404 response. Otherwise, you want to return a 500. You'd need to require NotFoundError from objection.
const { NotFoundError } = require('objection');
const Industry = require('<myIndustryModelLocation>');
const editIndustry = (req, res) => {
try {
return Industry
.query()
.patchAndFetchById(req.params.industryId, { ...req.body })
.throwIfNotFound();
} catch (err) {
if(err instanceof NotFoundError) {
return res.status(404).json({
error: 'NotFoundError',
message: `industry not found`,
});
}
return res.status(500);
}
};
I am creating a Nodejs and express based backend application and trying to handle error in a manner which is suitable for production systems.
I use async await to handle all synchronous operations in the code.
Here is a code snippet of router end points
app.get("/demo",async (req, res, next) => {
await helper().catch(e => return next(e))
console.log("After helper is called")
res.json(1)
})
function helper(){ //helper function that throws an exception
return new Promise((resolve, reject)=> reject(new Error("Demo Error")))
}
After all routes are defined I have added a common error handler that catches exceptions. To simplify it I am adding a simple function
routes.use( (err, req, res, next) => {
console.log("missed all", err)
return res.status(500).json({error:err.name, message: err.message});
});
I expect that the code after await helper() should not execute since the exception has been handled and response sent to frontend. Instead what I get is this error.
After helper is called
(node:46) UnhandledPromiseRejectionWarning: Error
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the
client
What is the correct way to handle error with async await?
You get After helper is called, because your code continues to execute since it did not return
Don't chain catch with async/await. You do that with Promise.
helper()
.then(data => console.log(data))
.catch(e => console.log(e))
You can handle error like:
app.get("/demo",async (req, res, next) => {
try {
await helper();
// respond sent if all went well
res.json(something)
catch(e) {
// don't need to respond as you're doing that with catch all error handler
next(e)
}
})
you can use try catch to handle the situation
app.get("/demo",async (req, res, next) => {
try {
await helper()
console.log("After helper is called")
res.json(1)
} catch(err) {
next(err)
}
})
function helper(){ //helper function that throws an exception
return new Promise((resolve, reject)=> reject(new Error("Demo Error")))
}
I'm new in Node and I've watched some of the tutorials from the Net.
My main problem is how I can show the result from the route I created. Well, I'm getting the correct data using the console but when I access it through browser it doesn't show the json encoded data.
Here is my product.js:
const express = require('express');
const router = express.Router();
const connection = require('../connection');
router.get('/',(req,res)=>{
res.send(products());
})
async function products(){
// console.log(products)
return await getProducts();
}
function getProducts(){
return new Promise((resolve,reject) =>{
connection.query('SELECT brand,description from products limit 100', function (err, rows, fields) {
if (err) throw err
resolve(JSON.stringify(rows))
})
})
}
module.exports = router;
Here is the console log result : http://prntscr.com/llxgk2
Here is the result from the postman: http://prntscr.com/llxgy3
You need to consume the promise. In your case, probably with then and catch handlers:
router.get('/',(req,res)=>{
products()
.then(products => {
res.json(products);
})
.catch(error => {
// ...send error response...
});
});
Note I've used res.json to send the response. You probably want to change your resolve(JSON.stringify(rows)) to just resolve(rows) and leave what to do with the rows to the caller. (res.json will stringify for you.)
You might also look at Koa, which is from the same people who did Express, which provides first-class support for async functions as routes.
How to purposely make a promise fail? Sometime I just skip the test and assume everything is fine, but I want to purposely make the promise to fail so that my catch is working.
exports.submitJob = async (req, res, next) => {
const { cv } = req.body
const userId = req.user._id
try {
if(!cv) {
//how to pass error to catch block?
}
const save_submission = new Submission({
userId,
cv
}).save()
} catch(e => {
res.json({
status: 0,
error: e
})
})
next()
}
You can throw new Error('<your string here>');:
Note that catch is not something to be used with function syntax - the proper syntax is catch (e) { /* block that uses e */ }
const submitJobWhichWillFail = async (req, res, next) => {
const cv = null;
try {
if (!cv) {
throw new Error('cv must not be falsey!');
}
const save_submission = new Submission({
userId,
cv
}).save()
} catch (e) {
console.log('res.json with error ' + e);
}
}
submitJobWhichWillFail();
use the throw statement, maybe?
if(!cv) {throw("cv is absent");}
user-defined exception-types (alike one commonly has them in Java or PHP) are also possible and recommended, because one barely can differ the type by a string and otherwise can easily check the typeof the exception in the catch block. just learned from the MDN, that one eg. can also throw DOMException and Error.