Suppose I have an api.js:
const {somefunction} = require('../controllers/some-controller');
app.get('/data/', somefunction);
some-controller.js:
exports.somefunction = async (req, res,next) => {
const someVariable = req.params.variable
try {
console.log('status',res.statusCode) **//status code is: 200**
const Details = await collectionName.find({}).exec()
res.send(Details)
} catch {
console.log('status',res.statusCode) **//Here also,status code is: 200**
next(ApiError.dbError('Internal Server Error')); //middleware for error handling gives output
//as ApiError { code: 500, message:
//'Internal Server Error' }
return;
}
};
Say I wrote some wrong variable name in res.send(Detaaal) and it goes to catch block
Here even in catch block status code is 200.
I'm trying to understand at what condition is status code different. Why is status code response on fail inside catch block didn't give me 500 status code.
200 is the default code and nothing has happened that would change that.
You haven't written code to change it and, while an exception has been thrown, you caught it, so it never hits Express' own catch logic which would set a 500 error.
The next function, on the other hand, does appear to change it, but you are looking at the value before running that function.
sorry about my poor english
your logic in here cause the problem you are getting everything in collection even its empty
const Details = await collectionName.find({}).exec()
when collection is empty there was no error just return empty array like this[]
You must create a condition to prevent an empty submission from being sent here is my solution
exports.somefunction = async (req, res,next) => {
const someVariable = req.params.variable
try {
console.log('status',res.statusCode) **//status code is: 200**
const Details = await collectionName.find({}).exec()
if (Details.length===0) {
return res.status(404).send();
}
res.send(Details)
} catch {
console.log('status',res.statusCode) **//Here also,status code is: 200**
next(ApiError.dbError('Internal Server Error')); //middleware for error handling gives output
//as ApiError { code: 500, message:
//'Internal Server Error' }
return;
}
};
Related
I have access to two APIs. The first (and better API) responds with 404 for certain types of input. If the first API responds with 404 I want to send a request to the second API.
The log shows this after a 404:
{
insertId: "0987654321"
labels: {2}
logName: "projects/my-awesome-project/logs/cloudfunctions.googleapis.com%2Fcloud-functions"
receiveTimestamp: "2022-11-01T21:41:50.570143002Z"
resource: {2}
textPayload: "Exception from a finished function: HTTPError: Response code 404 (NOT FOUND)"
timestamp: "2022-11-01T21:41:50.251844Z"
trace: "projects/my-awesome-project/traces/1234567890"
}
I'm guessing that the code will look something like this:
try {
async function callFirstAPI() {
const response = await got(url, options).json();
// if successful do stuff with the data
callFirstAPI();
}
} catch (error) {
if (error.textPayload.includes('404') {
callBackupAPI();
}
}
What doesn't work is this. Nothing logs when the API throws a 404.
try {
async function callFirstAPI() {
const response = await got(url, options).json();
console.log(response); // nothing logs
callFirstAPI();
}
} catch (error) {
console.error("Error!!! " + error); // nothing logs
}
Is catch not firing because a 404 response isn't considered an error? I.e., the callFirstAPI ran without crashing?
Your function in your try block isn't actually running.
You're just declaring the function, but never invoking it.
Try something like this:
try {
async function callFirstAPI() {
const response = await got(url, options).json();
console.log(response); // nothing logs
}
firstCallApi() // NOTE: ADD THIS LINE
} catch (error) {
console.error("Error!!! " + error); // nothing logs
}
I found my mistake. Here's the working code:
async function callFirstAPI() {
try {
const response = await got(url, options).json();
// if successful do stuff with the data
} catch (error) {
await callBackupAPI();
}
}
callFirstAPI();
I had try in the outer block, async in the middle block, and await in the inner block. The correct syntax is async in the outer block, try in the middle block, and await in the inner block. This is explained near the end of the async function documentation.
I hate to think how many years I've been using the incorrect syntax! This is the first time I've wanted to do something with a 404 response. The code will run with the incorrect syntax, it'll just never go to the catch block, i.e., errors won't be handled.
And the error log doesn't contain 404 so if (error.includes('404') won't do anything. Just put your backup code in the catch block and it'll run, no need for if.
I am using a special wrapper to handle requests error on Frontend.
My error handle (handle.js):
async (handle, customCatch = () => {}) => {
try {
await handle();
} catch (err) {
if (err.response) if (await customCatch(err)) return;
await notify.error('Error !');
}
};
My example use:
handle(async () => {
const users=await axios.get('/users')
})
If the axios requests which in the handle are any error notify will an error alert. It is basic a request. If there aren't any error it will get users information as expected. It prevents duplicate code. Automatically show an error with notify. If we don't use the handle method we would use like this:
try{
const users=await axios.get('/users')
}catch(err){
notify.error('Error !');
}
Okey, I think it is very useful. If we want to handle an error that we know how we would use it ? Other my example:
handle(async () => {
await axios.post('example/login',{password:'test',username:'test'})
}, (err) => {
if (err.response.status === 400) {
this.isWrongEmailOrPassword = true;
return true;
}
})
If it gets 400 error we want to set to isWrongEmailOrPassword variable. So we know that may get 400 error. We can set a custom error handler for custom request error code.
Is correct way this handle method ? How reasonable ? If this way isn't correct way what is correct way ? Thank you.
I am writing integration test for a nodejs/sails js application, where I have an Async controller method/route that throws error when input parameters are not provided.
I am using supertest to write integration test, everything works fine from my perspective, but when the test run error is written onto the console.
describe("a controller method", () => {
it("should throw error message", () => {
server('127.0.0.1')
.get('url to getData')
.set('Cookie', 'cookie token')
.end((err, res) => {
if(err) return done(err);
//console.log(res);
expect(res.status).to.equal(500);
expect(res.body).to.have.property('error', 'Provide a jobName');
done();
});
});
});
This following piece of code works fine cause I wrap this within an anonymous function and expect that function to throw. But I am not sure how to assert against those error.
it("throws", () => {
expect( function () {
server('127.0.0.1')
.get('some url')
.set('Cookie', 'cookie token')
}).to.throw;
});
The controller code looks like following. This is the method that is being called when URL end is requested.
getData : async (req, res) => {
let jobName = req.params.jobName || '',
buildId = req.params.buildId || '';
if(!jobName.trim() ){
return res.negotiate({error:'Provide a jobName'});
}
if(isNaN(buildId)) {
return res.negotiate({error:'Invalid build id supplied.'});
}
try {
let rawResult = await getData(jobName, buildId);
let promotions = formatData(rawResult);
let result = {
total : promotions.length || 0,
items : promotions
};
return res.json(result);
} catch(error) {
sails.log.error(`Request Parameter: Job name = ${req.param('jobName')} & build id = ${req.param('buildId')}`);
sails.log.error(error);
return res.negotiate({error: sails.config.errorMessage});
}
}
Why is the error being written to console ? What am I doing wrong here?
Any help/pointer is highly appreciated!!
How are you sending that error from express? In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next' function. http://expressjs.com/en/guide/error-handling.html
e.g
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
async returns a promise and although you have placed a try catch block, my guess is the error is not in the block. It is most likely due to the promise rejection not getting handled.
The below should be of help to you as applying to the sails context.
since async functions return a promise, you need to .catch() any
promise rejections and pass them along to next(). Express error
handlers are only triggered by errors passed to next(), not exceptions
that you throw. Source
And for promise()=> reject
I am using Vue.js with Vue-Apollo and initiating a User mutation to sign in a user. I am using the graph.cool service.
I have a request pipeline function setup to catch some errors, like an invalid email.
When the request is made with bad / invalid input, my error catch() fires (as expected) and in the network tab I can see the JSON for the custom errors messages. But how do I access these errors / response from within the catch if an error is triggered from graph.cool?
Example:
signin () {
const email = this.email
const password = this.password
this.$apollo.mutate({
mutation: signinMutation,
variables: {
email,
password
}
})
.then((data) => {
// This never fires on an error, so I can't
// show the user the errors in the network repsonse.
console.log(data)
})
.catch((error) => {
// Error in this part fires in the console
// but I'm unable to show the JSON response
// errors because the 'then()' above doesn't execute.
console.error(error)
})
}
I get the following error for an unrecognised user:
Error: GraphQL error: No user found with that information
at new ApolloError (eval at (app.js:956), :34:28)
at eval (eval at (app.js:1353), :139:33)
at
Any idea how to show the errors in the response from within the catch()?
I can literally see the errors I want to show to the user in the response on the network tab here:
...but I can't figure out how to do it.
Any help much appreciated! Thank you.
So, it looks as though I was handling this the wrong way by barking up the wrong tree.
The key to the answer was examining the error from the .catch() with console.dir(error). This revealed some useful keys...namely:
error.graphQLErrors[0]
So all in all, the corrected code looks like this:
signin () {
const email = this.email
const password = this.password
this.$apollo.mutate({
mutation: signinMutation,
variables: {
email,
password
}
})
.then(data => {
console.log(data)
})
.catch(error => {
console.log(graphQLErrorMessages(error))
})
}
The graphQLErrorMessages() function is a helper I wrote, so that I can reuse this in other .catch() blocks:
function graphQLErrorMessages (errorsFromCatch) {
const errors = errorsFromCatch.graphQLErrors[0]
const messages = []
if (errors.hasOwnProperty('functionError')) {
const customErrors = JSON.parse(errors.functionError)
messages.push(...customErrors.errors)
} else {
messages.push(errors.message)
}
return messages
}
It returns an array of error messages (which is what I needed) but you could format this any way you like.
It might be a little https://graph.cool specific in its logic (I'm not so sure), but I hope this ends up helping someone also stuck in a similar situation!
I may be misunderstanding your question so please comment and correct me if I am but it looks like you may be having trouble with Promises more than with Vue or GraphQL.
Just like in a try...catch statement, once you catch an error, your program will continue to execute unless you re-throw the error. For example:
This Catches
try {
codeThatThrowsAnError();
} catch(e) {
// Do Nothing
}
This re-throws
try {
codeThatThrowsAnError();
} catch(e) {
throw new Error("Err 135: I failed")
}
Similarly, in Promise land, you can either catch the error and move like you have in your example, or you can re-throw. What you may be missing is that anything you return from a catch statement will be used in the next then. For example:
somethingReturningAFailedPromise()
.then(doWork)
.catch((err) => {
return "I'm a New Value"
})
.then(console.log)
//=> "I'm a New Value"
It sounds to me like what you need is a data function that is more resilient to failure like the following:
const getUserProfile = (id) => {
return fetchUserData(id)
.catch((err) => {
logError(err);
return {};
})
}
I use Jest to test a function which generates a JSON Web Token. It seems that I can't assert the value since when I assert, the callback hasn't been executed yet.
const issueJWT = function issueJWT(req, res, next) {
jwt.sign(signUser, function (err, token) {
if (err) {
next(err);
return;
}
res.locals.token = token;
next();
});
};
This is my test, I mock the request and response, then assert the result:
test('Should return a JWT with proper value if nothing wrong happened', () => {
issueJWT(request, response, mockNext);
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
});
The error is:
TypeError: Cannot read property 'payload' of null
How to make it work?
According to my knowledge, I think the callback is at the task queue which
means it will be executed when nothing is in the event loop, right? I wanna find a way to defer my assertion, but don't know how...
Thanks for the tips, I use the done, now the test could pass, but the problem is, whenever there is a problem, the error message doesn't make any sense... Any problem to my solution?
test('Should return a JWT with proper value if nothing wrong happened', (done) => {
const callback = () => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
expect(tokenPayload).toHaveProperty('iss');
done();
};
issueJWT(request, response, callback);
});
The error is now:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Ok, so with some help from #felixKling getting me to actually read the docs, you need to do something like this:
test('Should return a JWT with proper value if nothing wrong happened', done => {
issueJWT(request, response, (e) => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
done();
});
});
I'm not on my dev box so I can't test this, but basically the idea is that you use the 'done' parameter to the test callback to signal that the test is waiting on async code. The test framework will basically wait for your test to call that callback before exiting.
In this case, your next() call from issueJWT is what we're waiting on firing before checking to see if the various objects were updated. If you were not using next() in your middleware, you'd likely need to mock whatever response method you're calling instead (e.g. response.end()) to do your tests.