Returning a response immediately with res.json - javascript

I have an express route which takes in some parameters, queries the database, and then returns some response.
I am using sequelize to query the db:
router.get('/query', function(req,res) {
var name = req.params.name;
var gid = req.params.gid;
// Query the db
models.user.find({ where: { name: name }}).then(function(user) {
models.group.find({ where: { id: gid }}).then(function(group) {
// if user found, return data to client
if (user) {
res.json({"user": user, "group": group});
}
});
}).catch(function(error) {
// catch any errors from db query
res.status(500).json({"error":error});
});
// Return a server error for any other reason
// This causes ERROR
res.status(500).json({"error":"Something went wrong. Check your input."});
});
But I keep getting the error on the last line:
Can't set headers after they are sent
It seems like the last line is ALWAYS run, even if it finds a user (which should return data to the client and be done).
Why doesn't res.json(..) immediately return to the client when a user is found? Since headers were already set, when the last line runs, it throws that error.

You need to only conditionally return an error. The line:
res.status(500).json({"error":"Something went wrong. Check your input."});
is always getting executed. The reason for this is that the function you pass to the find method is only called later in the event loop after the db responds. This means that when that call back is called you have already set the error on the response.
Your should either remove that line or decide when you want to return an error but don't return an error every time.

Remember javascript is asynchronous.
As soon you call this function
models.user.find({ where: { name: name }})
That last line is executed:
res.status(500).json({"error":"Something went wrong. Check your input."});
It seems you are trying to cater for 2 scenarios:
Bad request data from client - i.e. no gid given
Internal server errors - i.e. error with the database
I would recommend changing your catch function to something like this:
.catch(function(error) {
// catch any errors from db query
if (err === "Unable to connect to database") {
return res.status(500).json({ error: "There was an internal error"})
}
res.status(400).json({"error": "Bad input, please ensure you sent all required data" });
});
Have a read up on the list of standard HTTP status codes:
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

What #bhspencer said is right. You have to remove that last line.
That line probably gets executed before any query in the database.

You need to implement a return in
models.user.find({ where: { name: name }}).then(function(user) {
models.group.find({ where: { id: gid }}).then(function(group) {
// if user found, return data to client
if (user) {
res.json({"user": user, "group": group});
return;
}
});
}).catch(function(error) {
// catch any errors from db query
res.status(500).json({"error":error});
return;
});
Actually res.json( does not end the processing of node.js code execution without return statement.

Related

nodejs app crash on openai dall-e 2 api rejected request

I'm surely dumb, but I'm not able to figure out how to handle openai api rejected requests
( for the context, dall-e 2 is an image generator )
when user tries to generate forbidden images, my nodejs app just exits
async function start(arg) {
try{
// generate image
const response = openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
});
// on success response
response.then(res =>{
console.log("ok");
})
response.catch(err =>{
console.log(err);
});
} catch(e){
console.log(e);
}
}
it gives me something like that on the exit :
data: {
error: {
code: null,
message: 'Your request was rejected as a result of our safety system. Your prompt may contain text that is not allowed by our safety system.',
param: null,
type: 'invalid_request_error'
}
}
tried using response.catch and try catch without success, the app just exits everytime
I at least want to ignore this error in the first place
in a second hand, I would like to console.log the given message (data.error.message)
I don't know what to do to by honest, don't even understand why try catch isn't working
With the details given, my guess would be that the Promise returned by getImages is being rejected. You could debug this a bit by adding some additional logs into your .catch callback and catch statement.
How to do this really depends on what you're trying to do with this api, the code as it's currently written would log something and exit no matter what happens.
There's a couple ways to handle this
Use your .catch to handle the error. Utilizing promise chainability you can get something like this
openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
user: msg.author.id,
})
.catch((e) => {
if (e.data.error.message.includes('safety system')) {
return 'something'
}
console.error(e)
})
If you need the response object, the asnwer might be different. Looks like the openai package is built on axios and you can pass axios options into it. See https://axios-http.com/docs/handling_errors and the Request Options section of https://npmjs.com/package/openai
EDIT
I found my solution thanks to #JacksonChristoffersen
Basically I was getting http status 400
I just added request options from axios to validate http status smaller than 500
Here's the solution:
async function start(arg) {
try{
// generate image
const response = openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
},{
validateStatus: function (status) {
return status < 500; // Resolve only if the status code is less than 500
}
});
// on success response
response.then(res =>{
console.log("ok");
})
response.catch(err =>{
console.log(err);
});
} catch(e){
console.log(e);
}
}

How to solve nodejs uncaughtException: Connection already released error and MaxListenersExceededWarning?

I am building an express server to receive request (a dict with 10 items) from my react front end and then save the data to database. Below is my code. I found that my code is work and the query does save the record back to Db. But in each for loop, this error is returned in server. What cause this error and the MaxListenersExceededWarning?
The request data:
{{.....}, {.....}, {.....}, {.....}, {.....}} #10 item
Code:
connection.js:
const p = mysql.createPool({
"connectionLimit" : 100,
"host": "example.org",
"user": "test",
"password": "test",
"database": "test",
"multipleStatements": true
});
const getConnection = function(callback) {
p.getConnection(function(err, connection) {
callback(err, connection)
})
};
module.exports = getConnection
routers.js
router.post('/test', (req, res) => {
getConnection(function(err, conn){
if (err) {
return res.json({ success: false, error: err })
} else {
const dict = req.body;
Object.keys(dict).forEach(function(r){
#putting dict's value to query
query = "UPDATE ......;"
conn.query(query, function (err, result, fields) {
conn.release()
console.log(query)
if (err) {
console.log("err")
return res.json({ success: false, error: err });
}
});
});
}
});
return res.json({ success: true });
});
Error:
error: uncaughtException: Connection already released
Error: Connection already released
at Pool.releaseConnection (/home/node_modules/mysql/lib/Pool.js:138:13)
at PoolConnection.release (/home/node_modules/mysql/lib/PoolConnection.js:35:15)
at Query.<anonymous> (/home/routes/test.js:276:22)
at Query.<anonymous> (/home/node_modules/mysql/lib/Connection.js:526:10)
at Query._callback (/home/node_modules/mysql/lib/Connection.js:488:16)
at Query.Sequence.end (/home/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query._handleFinalResultPacket (/home//node_modules/mysql/lib/protocol/sequences/Query.js:149:8)
at Query.OkPacket (/home//node_modules/mysql/lib/protocol/sequences/Query.js:74:10)
at Protocol._parsePacket (/home//node_modules/mysql/lib/protocol/Protocol.js:291:23)
at Parser._parsePacket (/home//node_modules/mysql/lib/protocol/Parser.js:433:10)
(node:15881) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 finish listeners added. Use emitter.setMaxListeners() to increase limit
One connection (conn) is being retrieved from the pool, and is used to launch 10 queries in the forEach loop.
When the first query finishes to run, the first step of its callback is: conn.release(). The connection is released.
When the second query finishes to run, its callback also tries to release the connection,causing the error.
This problem might be solved in multiple ways:
Solve using a counter
In the callback of the database query, before calling call.release, check the number of queries already processed, and only close the connection when the last product is being processed.
const dict = req.body;
// initialize counter
let itemCount = 0
, errors = []
Object.keys(dict).forEach(function(r){
#putting dict's value to query
query = "UPDATE ......;"
conn.query(query, function (err, result, fields) {
// check whether this is the last callback
if (itemCount === dict.length-1) {
conn.release()
let result = errors.length ? { success: false, error: errors } : { success: true }
res.json(result)
}
// increment counter
itemCount++
console.log(query)
if (err) {
console.log("err")
errors.push(err)
}
});
});
Edit: There is also an issue with the res.json calls: inside the code in the question, res.json({ success: true }) is always executed, without waiting for the queries' execution results. The modified code sample above calls res.json only once after the execution of all queries, this is the only place where res.json should be called. This implies modifying the client-side code so that it can handle an array of errors, rather than only one error.
Solve by using a recursive function instead of for loop.
It is not a good practice to use for loops for the execution of asynchronous code. You might run into Maximum call stack size exceeded errors whenever the data volume gets too large.
Instead, create a recursive function (e.g. updateDictItem) to process one update query at a time. Read more about the asynchronous patterns in node.js in this article.
Other possible enhancements
Rather than firing ten database queries, it is worth considering grouping all the updates in one MERGE update statement, otherwise doing all the updates in a TRANSACTION.

node.js token deletion promise

I have a delete route for my api that deletes tokens stored in our mongodb. I am currently writing a check that makes sure the token is not associated with another object before it is processed for deletion.
function checkTokenIsNotUsed(req) {
console.log('TEST ' + req.params.token);
objectDB.findObject('artefact', {"data.token": req.params.token})
.then(function(result) {
console.log('findObject result' + result);
if (isDefined(result)) {
console.log('Artefact Exists');
serverError(res, 'Cannot delete token as it is associated with an artefact');
} else {
console.log('Token not being used by an artefact, okay for deletion');
};
})
.catch(function(err){
console.error('Token CHECK DELETE error: '+err.message);
serverError(res, 'err.message');
});
return(result)
};
//
// Token deletion
//
app.delete('/:token', function(req, res, next) {
checkTokenIsNotUsed(req)
.then(function(results){
return tokenModel.findOne({token: req.params.token});
})
As it stands, when I hit the route the checkTokenIsNotUsed function is called, and prints 'TEST + <token>' to the console, and then kicks out to cannot read property 'then' of undefined.
I have checked my query in the mongo shell independently of the platform interface that I am using and the query works as expected. The api route also works as expected without the checkTokenIsUsed function active.
Any ideas as to why the promise for objectdb.findObject() is not returning a result? It is intended to return a list of objects or nothing at all, and does as expected in other locations in this file.
-findObject is properly exported + imported from its respective location
-the query is correctly structured and works in mongo shell.
-the token itself is being printed just before this function is run, so checkTokenIsNotUsed is getting called and being executed.
-I don't really care that the conditional is just printing to the console at the moment, as my objective right now is to merely enter into that part of the function.
EDIT added
findObject = function(objType, query, options) {
return new Promise(function(resolve,reject) {
var populateOptions;
var findQuery = {};
var findOptions = {
sort: 'metaData.createTS',
page: 1,
limit: 50
};
var objectDef = getObjectDef(objType);
if (!isDefined(objectDef)) return resolve(null);
// Make sure query exists and has objType set
if (isDefined(query)) findQuery = query;
if (!isDefined(findQuery.objType)) findQuery.objType = objType;
if (isDefined(options)) {
if (isDefined(options.sort)) findOptions.sort = options.sort;
if (isDefined(options.page)) findOptions.page = toInt(options.page);
if (isDefined(options.limit)) findOptions.limit = toInt(options.limit);
}
if (isDefined(objectDef.populate)) populateOptions = objectDef.populate;
else populateOptions = "";
objectDef.model.find(findQuery, null, findOptions)
.populate(populateOptions)
.exec(function(error, list) {
if (error) reject(error);
else resolve(list);
});
});
};

How to throw an error inside the pre handler in Hapi.js

I started using v17 of Hapi.js and I am running into issues when using the pre-handler.
I want to save a user into a database, but first I use the pre-handler to check if a user already exists. If the user exists, I want to throw an error. The structure of my route is as so...
module.exports = {
method: "POST",
path: "/users",
config: {
auth: false,
pre: [{ method: verify_unique_user}],
handler: create_user.create
}
}
The content of verify_unique_user is...
async function verify_unique_user(req, h) {
await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
},
(err, user) => {
if (user) {
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
}
});
return req;
}
Let's assume the user already exists in the database. Then an error will be thrown from either of the if statements. When this happens, I get the following error...
events.js:167
throw er; // Unhandled 'error' event
^
Error: Username taken!
at User.findOne (/Users/ericbolboa/Desktop/Warble/server/src/users/util/user_function.js:16:16)
This crashed my server. This is not what I want. If I throw an error in my handler function, the response looks like this...
{
"statusCode": 400,
"error": "Bad Request",
"message": "error"
}
But whenever I throw an error in the pre-handler, my server crashes. How can I throw errors properly?
Not sure if this is the source of the issue but you can simplify the async/await instead of using the callback
async function verify_unique_user(req, h) {
const user = await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
});
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
return req;
}
Take a look at the toolkit(h) and options.response.failAction of route.
A route can set response.failAction in options. There, you can format error messages, and send response, however you please. That includes errors thrown from pre handlers.
Edit: Every pre-handler can have it's own 'failAction' handler. You must do a response(...).takeover() if you want to halt the chain.

catch error: structure (127 [object] [object]) on neo4j cypher session

I have had a similar error before but that was caused by a syntax error in the .run statement.
I have looked this code over and over and can't find a syntax so I think something else is going on. This function is called by the passport deserializer and the value of "id" was confirmed using node-inspector. however, no matter which type of MATCH query I use, I get the same .catch error.
I have tried WHERE option and the direct option...they both work in the neo4j browser. Can someone see what I am not seeing
router.getUserByID = function (id, callback) {
session
.run ("MATCH (user {id(user) : {paramUserID}}) RETURN user",{paramUserID: parseInt(id)})
.then (function(result)
{
if ( !result.records[0])
{
console.log("unknow user by id");
session.close();
if (typeof callback==="function") {
return callback(null,false);
}
} // end of if not found
else
{
console.log("user by id found");
session.close();
if (typeof callback === "function") {
return callback(null,result);
}
}
})
.catch(function(err)
{
console.log("catch error: "+err);
});
} // end of get user by id
You cannot reference the Neo4j id of the node that way, you have to use a WHERE clause:
MATCH (user) WHERE id(user) = {paramUserID} RETURN user;
If you have an application id (almost always a good idea), then you could do:
MATCH (user {uuid: {paramUuid}}) RETURN user;
It would be better with a label though, because different entities could have the same id, and you can use a unicity constraint (which also indexes the values, so the query is faster):
CREATE CONSTRAINT ON (n:User) ASSERT n.uuid IS UNIQUE;
// Later
MATCH (user:User {uuid: {paramUuid}}) RETURN user;

Categories

Resources