Background: In Node, it is common to pass an error object to a callback function in async calls as well explained in Error handling in Node.js by Joyent. A standard error object contains a name, a message, a stack trace and possible additional properties. Passing or throwing strings or plain objects as errors is considered a bad practice. With Socket.io, data can be transmitted from client to server and vice versa by emitting events and having an optional response callback. This opens up multiple ways to pass and handle errors.
Question: If a socket.io event from a client causes an error on the server, what is the best practice to inform the client about this? A plain error object in response? A separate error event and listener? How the error should be structured? Is there any best practices like is the case with Node? What are you using?
Example: Imagine socket.io used for login. A client logs in by emitting a username and a password like below:
socket.emit('login', {user: 'Foo', pass: 'Bar'}, function (response) {
...
});
On a successful login, the response contains a session token. But what if the username or the password do not match? Several approaches come to mind:
Approach 1 - plain error object: The response could contain a property error having a plain error-like object as its value, with standard name and message properties and possible additional ones:
{
error: {
name: 'InvalidUsernameOrPasswordError',
message: 'Username or password was invalid.',
usernameExists: false
}
}
The client thus tests if response contains an error and if not, continues with the login procedure:
socket.emit('login', {user: 'Foo', pass: 'Bar'}, function (response) {
if (response.hasOwnProperty('error')) {
// handle error
}
// do something with response.token
});
Approach 2 - simple string: Similar to Approach 1, but the error property is just a plain string representing the name of the error.
{
error: 'InvalidUsernameOrPasswordError'
}
This approach is light and sufficient in this naïve example but lacks ability to pass additional data about the error.
Approach 3 - separate event: The server could emit and the client listen additional events to handle possible outcomes:
socket.on('loginError', function (error) {
// handle error based on error.name and error.message
});
socket.on('loginSuccess', function (data) {
// handle successful login with data.token
});
socket.emit('login', {user: 'Foo', pass: 'Bar'});
This approach feels the most explicit and pure under the event emitting paradigm but requires multiple event handlers.
Conclusions: There seem to be many possible ways to pass and handle errors. Any experiences, thoughts or feels?
It depends on your "clients". If you have end users like people using an application they really don't care so much about how you send the error. At the end you are going to have a text on the frontend saying they are wrong. So you are the one who have to select which way you prefer for implementing the error handling on the backend.
If your clients are not end users like the case above, imaging you are creating an application or library and you have to say there was an error. You should return as much as information you can in order for people who use your application identify where you error is.
So at the end:
-For customers: You will show your errors on the frontend so that ups to you how you want manage the error.
-For developers: You should show as many error information as you can. Showing the stack trace, error code...
Related
Am using express js on node for my api service ! In which am using sequelize for query handling purposes !
So in some usecase like creating record, or updating record its simply returning "1" or sometimes nothing !
In this case , am just using
res.sendStatus(200);
or sometimes
res.send("success");
Is there any better way or this is the correct way to handle ? Or should in need .end() in order to end the process ??
which is a good way to handle these kind of useless responses which we dont need to send back ?
This is where Status 204 comes in to play: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success
It states: everything is OK (like in 200), but there is simple no need to send a body.
Using express, it's just as simple as: res.sendStatus(204)
Usually a json is send back to the client with different information in it that lets the client-side know that the operation that they request through the api is successfull or failed. for e.g. here, you can define a standard json response like this
//in case of success of the request
{
requestStatus: 1,
...other data if you want to like new Id of the created data/updated data or info about it if you want or depending upon use case
}
or
// in case of failure of the request
{
requestStatus: 0,
...other data if you want to like reason for failure, error message or depending upon your use case
}
Just add this line of code, should be fine:
//For all bad endpoints
app.use('*', (req, res) => {
res.status(404).json({ msg: 'Not a good endpoint, why are you here? Go play FIFA.'})
})
If you want you can generate an error HTML file, but since it's back-end, JSON format is strongly suggested. You can also add success: false for more clarity.
I have an updateDocument method in a class for a service layer in a node.js, express, mongoose application. I'm wondering what is the best practice for handing cases where the update method didn't update a document, for example if the wrong id is passed in.
Version 1: If the update wasn't successful, return an object with success: false:
async updateDocument(id, updates) {
const output = await this.DocumentModel.updateOne({ _id: id }, updates);
let message = 'Something went wrong';
let success = false;
let updatedItem = null;
if (output.nModified) {
message = 'Successfully updated document.';
success = true;
updatedItem = await this.getDocument(id);
}
return { message, success, updatedItem};
}
Version 2: If the update wasn't successful, throw an error:
async updateDocument(id, updates) {
const output = await this.DocumentModel.updateOne({ _id: id }, updates);
let updatedItem;
if (output.nModified) {
updatedItem = await this.getDocument(id);
} else{
throw new Error("The document wasn't updated")
}
return updatedItem;
}
Do you always throw an exception when the input, such as a bad id, isn't correct? Or could you return information about the update being a success or not? As newbie node.js developer, I'm not sure I am grasping the full picture enough to recognize problems with either method.
There is no golden way, only principles that lead to robust and well-maintainable software.
Generally, you should use a try-catch-statement for all kinds of errors that are not in your control (connections, disk space, credentials, ...) . The errors should then be handled as soon as possible, but not before. The reason for this is that you often don't know, yet, how to handle an error in an appropriate manner at a lower layer.
For logical "errors" that you can expect (wrong input format, missing username, unknown options, ...), you should use an if-statement or a validation function and then throw an error, if anything is not as expected.
In your case, you should check, if the methods updateOne or getDocument can throw errors. If yes, you should wrap these functions with a try-catch-statement.
A few more tips:
Both versions of your code are good. But I would prefer version 2 because it is more concise.
If you are sure that there is always an output object, you can destruct the nModified property like this:
const { nModified } = await this.DocumentModel.updateOne({ _id: id }, updates);
If you use a negative if-statement, you can reduce the depth of indentation and you can use const variables:
if (!nModified) {
throw new Error("The document wasn't updated")
}
const updatedItem = await this.getDocument(id);
Now, you could also directly return this.getDocument(id) and don't need the variable updatedItem anymore.
You can finally handle your errors in your controller classes.
You can use custom error classes to be consistent in your error handling and error messages all over your app.
I hope this is at least a bit helpful.
References
These are some similar questions with good answers. But you need to take care because many code examples are not in modern JavaScript.
A general discussion about the pros and cons of Error-Handling vs.
if-else-statements is done here:
What is the advantage of using try {} catch {} versus if {} else {}
Error-Handling in Node.js is discussed here in this thread:
Node.js Best Practice Exception Handling
It seemed like there were a lot of different opinions on this and not one go-to method. Here's some information I found and what I ended up doing.
When to throw an exception?
Every function asks a question. If the input it is given makes that question a fallacy, then throw an exception. This line is harder to draw with functions that return void, but the bottom line is: if the function's assumptions about its inputs are violated, it should throw an exception instead of returning normally.
Should a retrieval method return 'null' or throw an exception when it can't produce the return value?
Answer 1:
Whatever you do, make sure you document it. I think this point is more important than exactly which approach is "best".
Answer 2:
If you are always expecting to find a value then throw the exception if it is missing. The exception would mean that there was a problem.
If the value can be missing or present and both are valid for the application logic then return a null.
More important: What do you do other places in the code? Consistency is important.
Where should exceptions be handled?
Answer 1: in the layer of code that can actually do something about the error
Exceptions should be handled in the layer of code that can actually do something about the error.
The "log and rethrow" pattern is often considered an antipattern (for exactly the reason you mentioned, leads to a lot of duplicate code and doesn't really help you do anything practical.)
The point of an exception is that it is "not expected". If the layer of code you are working in can't do something reasonable to continue successful processing when the error happens, just let it bubble up.
If the layer of code you are working in can do something to continue when the error happens, that is the spot to handle the error. (And returning a "failed" http response code counts as a way to "continue processing". You are saving the program from crashing.)
-source: softwareengineering.stackexchange
Answer 2: Handle errors centrally, not within a middleware
Without one dedicated object for error handling, greater are the chances of important errors hiding under the radar due to improper handling. The error handler object is responsible for making the error visible, for example by writing to a well-formatted logger, sending events to some monitoring product like Sentry, Rollbar, or Raygun. Most web frameworks, like Express, provide an error handling middleware mechanism. A typical error handling flow might be: Some module throws an error -> API router catches the error -> it propagates the error to the middleware (e.g. Express, KOA) who is responsible for catching errors -> a centralized error handler is called -> the middleware is being told whether this error is an untrusted error (not operational) so it can restart the app gracefully. Note that it’s a common, yet wrong, practice to handle errors within Express middleware – doing so will not cover errors that are thrown in non-web interfaces.
-source; Handle errors centrally, not within a middleware
More: Best Practice Node.js: Error Handling
So it seems like these two principles disagree. #1 says to handle it right away if you can. So for me it would be in the service layer. But the #2 says handle it centrally, like in the server file. I went with #2.
My decision: throw the error in a custom error class
It combined a few methods people suggested. I am throwing the error, but I'm not "log and rethrow"-ing, as the answer above warned against. Instead, I put the error in a custom error with more information and throw that. It is logged and handled centrally.
So first in my service layer this is how an error is thrown:
async addUser(user) {
let newUser;
try {
newUser = await this.UserModel.create(user);
} catch (err) {
throw new ApplicationError( // custom error
{
user, // params that are useful
err, //original error
},
`Unable to create user: ${err.name}: ${err.message}` // error message
);
}
return newUser;
}
ApplicationError is a custom error class that takes an info object and a message. I got this idea from here:
In this pattern, we would start our application with an ApplicationError class this way we know all errors in our applications that we explicitly throw are going to inherit from it. So we would start off with the following error classes:
-source: smashingmagazine
You could put other helpful information in your custom error class, even maybe what EJS template to use! So you could really handle the error creatively depending on how you structure your custom error class. I don't know if that's "normal", maybe it's not SOLID to include the EJS template, but I think it's an interesting concept to explore. You could think about other ways that might be more SOLID to dynamically react to errors.
This is the handleError file for now, but I will probably change it up to work with the custom error to create a more informative page. :
const logger = require("./logger");
module.exports = (err, req, res, next) => {
if (res.headersSent) {
return next(err);
}
logger.log("Error:", err);
return res.status(500).render("500", {
title: "500",
});
};
Then I add that function to my server file as the last middleware:
app.use(handleError);
In conclusion, it seems like there's a bit of disagreement on how to handle errors though it seems more people think you should throw the error and probably handle it centrally. Find a way that works for you, be consistent, and document it.
This question is similar, but answers are specific to sails: NodeJS best practices: Errors for flow control?
I come from a background working on REST APIs in C#. In C#, when there was an operational "error" (I'm intentionally using error in quotes), we just returned a constant or key to a lookup table of messages. Depending on the message that was returned, the routing layer would return an appropriate status code to the user (200, 404, etc.).
Take logging in. Consider that someone logs into a server, and the account does not exist. This is not an unusual thing to happen, so it doesn't make much sense to throw an error.
Now, I'm writing Node applications and I'm frustrated. It seems like common practice is to just throw errors everywhere when anything unexpected happens. This makes sense for things like the database failing to retrieve an account which is known to exist, or for catching syntax errors. But for something like a user entering the wrong username/password, throwing an error seems like overkill.
Then consider a case where you have error-reporting middleware. For syntax errors, you might want to log a stack trace, when you would never want to do that for something like a wrong password. By using errors for control flow, a user entering a bad password could be using up the same server resources as logging a critical database exception. You could extend the Error class and switch on the type of error, but that seems sloppy.
Finally, some solutions just directly return ExpressJS reponses with status codes, but I don't always want the same status code for every case where a user isn't found in the database. Maybe I want to return a 404 for a user profile page, and a 400 for a failed login.
Has anyone else faced this problem, and if so, how have you handled it (pun intended)?
UPDATE
Here's some code to demonstrate what my solution would look like.
I've invented a new class called Goof, which is like an error but it is used in situations where it is a likely outcome. For example, bad login credentials would be a type of Goof:
// constants.js.
const WRONG_PASSWORD = 1;
const USERNAME_DOES_NOT_EXIST = 1;
// goof.js.
class Goof {
constructor(message) {
this.message = message;
}
}
// Inside express route.
const username = req.body.username;
const password = req.body.password;
const result = await signInUser(username, password);
if(typeof(result) === Goof) {
if(result.status === constants.WRONG_PASSWORD) {
res.sendStatus(400);
return;
}
if(result.status === constants.USERNAME_DOES_NOT_EXIST) {
res.sendStatus(400)
return;
}
else {
throw new Error('A Goof was returned but not handled properly');
}
} else {
res.json(result);
}
What are the advantages/disadvantages of this over throwing and catching errors?
I'm currently writing a public REST service in Node.js that interfaces with a Postgres-database (using Sequelize) and a Redis cache instance.
I'm now looking into error handling and how to send informative and verbose error messages if something would happen with a request.
It struck me that I'm not quite sure how to handle internal server errors. What would be the appropriate way of dealing with this? Consider the following scenario:
I'm sending a post-request to an endpoint which in turn inserts the content to the database. However, something went wrong during this process (validation, connection issue, whatever). An error is thrown by the Sequelize-driver and I catch it.
I would argue that it is quite sensitive information (even if I remove the stack trace) and I'm not comfortable with exposing references of internal concepts (table-names, functions, etc.) to the client. I'd like to have a custom error for these scenarios that briefly describes the problem without giving away too detailed information.
Is the only way to approach this by mapping every "possible" error in the Sequelize-driver to a generic one and send that back to the client? Or how would you approach this?
Thanks in advance.
Errors are always caused by something. You should identify and intercept these causes before doing your database operation. Only cases that you think you've prepared for should reach the database operation.
If an unexpected error occurs, you should not send an informative error message for security reasons. Just send a generic error for unexpected cases.
Your code will look somewhat like this:
async databaseInsert(req, res) {
try {
if (typeof req.body.name !== 'string') {
res.status(400).send('Required field "name" was missing or malformed.')
return
}
if (problemCase2) {
res.status(400).send('Error message 2')
return
}
...
result = await ... // database operation
res.status(200).send(result)
} catch (e) {
res.status(500).send(debugging ? e : 'Unexpected error')
}
}
I wanted to know does YUI3 provides any way to try and catch errors functionality, where in after the error is captured we can show some customized error alert and simultaneously log the error at server side with the error exceptions and other details.
Also if this functionality is not there in yui3 then which all frameworks do one need to use to do this and which all are compatible with YUI.
I'm not aware of YUI3 providing exactly what you're after out-of-the box.
You can split your question into two parts:
Capturing errors
You either wrap your code with try/catch blocks or use a global error handler. It looks like YUI3 doesn't yet directly handle this (http://yuilibrary.com/projects/yui3/ticket/2528067) but handling it shouldn't be too hard, you'll just have to test for browser differences.
Sending Error data to the server
You ought to be able to use Y.IO to send back the error data to the server. It looks like you get errorMsg, url, lineNumber given to you, so you can just send them back to the server:
YUI().use("io-base",function(Y){
window.onerror = function(errorMsg, url, lineNumber){
Y.io("/errorHandler.php", {
data: {
errorMsg: errorMsg,
url: url,
lineNumber: lineNumber
}
});
alert("Sorry, something bad happened");
};
console.log("handler registered");
//now trigger an error
a.b.c="banana";
});
That seems to work here: http://jsfiddle.net/J83LW/
I'l leave the customized alert to you, I've left an alert here as a basic example of handling this