I have the following .then example chain in my React Native client code, currently without a .catch because I am looking for advice on how to set it up:
await getUserInfo(userId, JSON.stringify(ratingsQueryRatingType), 1)
.then(async userRatingData => {
await findMatchHistory(userId, '', 3)
.then(async matchHistoryData => {
These functions make calls to my NodeJS server. The NodeJS server then sends back the data.
I am trying to find out how I can effectively send back an error from the server to the client, and have the .catch part in the client handle that (e.g. with Alert.alert(error)).
I tried to throw an error on my server as follows but then on my server I get Unhandled promise rejection. It appears that it does not send the error back to client.
// Other code before this part
if (response==='Success') {
return res.status(200).json({'status': 'success'})
} else {
throw 'Match record was not confirmed successfully'
}
Or is it common practice to send response objects from the server (instead of Errors) and then handling those on the client with some kind of if-statement, such as the following?
if (results['status']==='success') {
// Code
} else if (results['status']==='failure') {
// Code
}
I do read about .then chaining with .catch being an attractive option so it feels like this would not be the correct solution..
I think we should send error-codes to the client instead of sending message although you can do it too. you can check the status code based on the error occurred on the backend -> https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
Also, on client side you can use interceptor and create an error-handler service layer and based on the code you will be sending to the client side you can handle that. you can follow these steps to setup one for your app : https://bilot.group/articles/using-react-router-inside-axios-interceptors/
And, On the backend side if there is an error try logging it to log files on your server.
// Other code before this part
if (response==='Success') {
return res.status(200).json({'status': 'success'})
} else {
res.status(based on what happened on server).json({'status': 'failure'})
}
For .then channing instead you should use async/await. for better understanding how to use them and convert chain to asyn/await. This doc contains the step-by-step guide: https://advancedweb.hu/how-to-refactor-a-promise-chain-to-async-functions/
Related
After rendering my index.html (which works fine), I would like to send some additional data via sockets. For that, I would need a promise for the rendering process. At the moment, the code runs synchron. The socket data is sent and moments later the data is overwritten due to the later ending rendering process. Looking for something like:
res.render('index', {title: "XYZ"})
.then(function(){
//.. do something
});
Is there a different approach? Or is the only solution to ask for the data via the client?
Thanks for any help!
Does the render function has a promise?
The documentation doesn't mention one, so presumably not.
For that, I would need a promise for the rendering process.
Not necessarily, just some kind of notification that the data had been sent. Promises are one kind of notification, but not the only kind.
The docmentation shows that render will call a callback function with the rendered HTML, so you could use that callback to send the HTML along with whatever you want to have follow it:
res.render("index", {title: "XYZ"}, function (err, html) {
if (err) {
// ...send an error response...
return;
}
res.send(html);
// ...send your other stuff here...
});
But if you want a promise, you could use util.promisify on res.render. It's a bit of a pain because promisify doesn't make handling this straightforward, so you have to use bind:
const resRender = util.promisify(res.render.bind(res));
// ...
resRender("index", {title: "XYZ"})
.then(html => {
res.send(html);
// ...send your other stuff here...
})
.catch(err => {
// ...send an error response...
});
You've said you're sending further information "via sockets." That makes it sound to me like the further information you're sending isn't being sent via the res response, but via a separate channel.
If so, and if you want to wait to send that until the response is sent, you can start your socket sending in response to the finish event on the response:
res.on("finish", () => {
// Send your socket message here
});
res.render("index", {title: "XYZ"});
(Remember that an Express Response object is an enhanced version of the Node.js ServerResponse object, which is what provides this event.)
But even then, all that means is that the data has been handed over to the OS for transmission to the client. From the documentation:
...this event is emitted when the last segment of the response headers and body have been handed off to the operating system for transmission over the network. It does not imply that the client has received anything yet.
I don't think you have anything beyond that to hook into.
I am triggering a bad request on purpose from my backend. The backend using express is answering properly as expected with:
[...]
.catch((error) => {
res.statusCode = 400;
res.json({
msg: "This is some error",
err: error,
});
});
My question is: Is the 400 always showing up in browser console? I thought I handled the error the right way.
And why do I have to retrieve the data in the frontend with
// 400:
function (data) {
var data = data.responseJSON.msg);
}
instead of (similar to 200:)
// 400:
function (data) {
var data = data.msg);
}
I am just wondering, if I do something wrong.
Thanks for any advice
When fetching data asynchronously, any response other than a 2** response code gets handled by a .catch block. So any other response whether it be 4** or 5** gets caught by the catch block and for that reason if you're using a library like Axios or the likes the response from the backend will be in the error.response or data.response in your case.
I wasn't aware of what you're using to carry out the asynchronous fetching of data i.e. Fetch Api so my answer is a generic response and also is the above code complete?
In my experience any error from whatever weither it be am asynchronous call that generates an error always gets logged to the console, but you can from the front end handle those errors in the catch block
The problem is when the console.log tries to output the error, the string representation is printed, not the object structure, so you do not see the .response property or in your case the .responseJSON
By catching your error with an catch block .catch() or if you using async / await you should, usually, wrap the code inside of an try / catch to catch the error otherwise it will always output this red error in the console
Issue clarification
When we use .emit() or .send() and we also want to confirm message reception (so called acknowledgements) we simply write something like this:
socket.emit('someEvent', payload, callback);
What this question is all about is a callback part. That's the great stuff as it allows to generally send back some data as a response with no extra events emitted. All that server needs to do is to handle the request in a proper way:
socket.on('someEvent', (payload, callback) => {
doSomeStuff();
callback(someData);
);
That works just fine when we deal with a success case. But what shall we do in these cases:
1) Callback was not sent from the client side / callback's not a function and there's a need to respond from the server side with something like 'Error: no callback is provided. Usage: ...'
Example:
Client side - socket.emit('someEvent'); or socket.emit('someEvent', 1);
Server side - socket.on('someEvent', callback => callback());
or
2) While handling the request something went wrong (e.g. an unsuccessful validation result) and we need to report this in a way like: 'No payload is provided or it is invalid'
Example:
Server side -
socket.emit('someEvent', payload, callback => {
checkPayload();
callback(someData);
});
Client side - socket.on('someEvent', invalidPayload, callback);
Question: is there a mechanism to create custom callback from responder's side?
My workings and workarounds
1) As for the missing callback or that one which is not a function I've concluded that I can only validate it and then invoke it only in case of its validity. So the server side is undergoing some changes:
socket.emit('someEvent', callback => callback instanceof Function && callback()); //check callback correctness
Pros: there won't be an internal error if a callback is not a function as expected.
Cons: in case of invalid callback a client won't be noticed about it.
2) As for the case when we need to send some error back I've only found a workaround to return a specific, agreed in advance, falsy value like null so that it means that no data can be returned.
socket.emit('someEvent', payload, callback => {
checkPayload();
callback(someData || null); //send falsy, error-like value instead
});
Pros: a client will be noticed about some error by getting null.
Cons: from server side there's no simple middleware function that validates the input data and returns error before the main logic is being executed.
I've thought about middlewares for reaching the needed functionality, but there's no, so to say, 'event level middlewares' yet, only on the whole namespace and socket levels. Shall I try to filter events by their names on the socket level to attach the needed functionality and send error in a way like next(new Error(...));? In this case there can be a work with error event listening, I guess.
socket.io / socket.io-client versions used: 2.3.0
1) Callback was not sent from the client side / callback's not a function and there's a need to respond from the server side with something like 'Error: no callback is provided. Usage: ...'
The client and server have to agree how to do this. If the client doesn't provide a callback, then the server argument will be undefined so you can detect that from the server.
So, the proper way to do it is this:
// client
socket.emit('someMsg', someData, function(response) {
console.log(`Got ${response} from server`);
});
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
fn("got your data");
}
});
});
So, if the client does not pass the callback, then fn on the server side will be undefined. So, you are correct to test for that before calling it.
2) As for the case when we need to send some error back I've only found a workaround to return a specific, agreed in advance, falsy value like null so that it means that no data can be returned.
Yes, you have to agree in advance how to send an error back. The cleanest way to send an error back would probably be to wrap your response in an object and use a .error property on that object.
// client
socket.emit('someMsg', someData, function(response) {
if (response.error) {
console.log(`Got error ${response.error} from server`);
} else {
console.log(`Got data ${response.data} from server`);
}
});
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
// no error here
fn({error: null, data: "Got your message"});
}
});
});
What you're seeing here is that socket.io is not really a request/response type protocol and socket.io has tried to shoehorn in a bit of a response around which you have to build your own structure.
Or, you can send an error object if there's an error:
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
// send an error here
fn({error: new Error("xxx Error")});
}
});
});
From server side there's no simple middleware function that validates the input data and returns error before the main logic is being executed.
I don't really understand what you're trying to use middleware for or to validate? the only place this data is present is on your message handler so any server-side validation you want to do on what the client sent needs to be there. You can certainly do that validation before you've send a response.
Shall I try to filter events by their names on the socket level to attach the needed functionality and send error in a way like next(new Error(...));? In this case there can be a work with error event listening, I guess.
Socket.io doesn't work like Express and I don't really see why you'd try to make it work that way. There is no next() involved in receiving a socket.io message so I'm not sure what you're trying to do there. There is an option for middleware when the socket.io connection is first made, but not for subsequent messages sent over that connection.
Is there a way to send response from server even if no callback is provided from client side?
If the client does not provide a callback, then the only way to send a response back to the client would be to send another message. But, the whole point of sending a response is if you have a cooperating client that is listening and expecting a response so the client may as well use the callback if they want the response. If the client doesn't want the response and won't code anything to receive it, there's nothing you can do about that.
I am trying to build a messaging web app using socket io and redux, and this post comes across. I can't comment on the post to ask questions, so I am posting a new question asking about the answer in that post(sorry if I violate stackoverflow quideline, but there seems to be no way for me to comment.) The part I don't understand is
In socketClient.js
emit(event, data) {
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
return this.socket.emit(event, data, (response) => {
// Response is the optional callback that you can use with socket.io in every request. See 1 above.
if (response.error) {
console.error(response.error);
return reject(response.error);
}
return resolve();
});
});
}
I don't quite understand the callback funciton in this.socket.emit. From what I understand, the call back function will be executed on server side, when the server receive the event, data and callback funciton from client. If that's the case, what does the return do in the callback function? what should response be? and what should this.socket.emit return? and how can the server resolve a promise on client side?
I don't quite understand the callback function in this.socket.emit.
When you pass a callback as the third argument to socket.io's .emit() method, it tells socket.io that you want an acknowledgement that the server has received the message and an optional response back from the server. That callback will be called after the server has received your message and called a callback on its end and the server has the option of sending a response back with that.
The return statements inside that callback are only for flow of control. They just cause code to stop executing in the callback. People tend to forget that reject() and resolve() are just function calls. They don't cause the rest of the function to stop executing.
The code you show could have been written like this with only the one return statement to return the promise:
emit(event, data) {
return new Promise((resolve, reject) => {
if (!this.socket) {
reject('No socket connection.');
} else {
this.socket.emit(event, data, (response) => {
if (response.error) {
console.error(response.error);
reject(response.error);
} else {
resolve();
}
});
}
});
}
Here, it uses if/else to control program flow rather than using return to stop execution of the function early. Either can work - personal preference.
If that's the case, what does the return do in the callback function?
It just stops further code from executing inside the callback function. Just a control flow thing. There is no meaningful value to return from the callback.
what should response be?
response will be whatever the server sent as part of the acknowledgement.
what should this.socket.emit return?
this.socket.emit() happens to return the socket itself (allows for method chaining). But, that's not relevant here because there's no point in returning any value from a Promise executor function. If you do return a value, it is not used by the Promise in any way. Basically, there's too many return statements in that original code. They lead one to think the return value is relevant when it isn't. They do no harm, but do cloud up the proper intent and meaning of the code.
and how can the server resolve a promise on client side?
The server doesn't actually resolve a promise on the client side. The client defines a promise that will resolve when it gets a response back from the server. So, when that response comes back from the server, the client's own callback code that receives that response, then resolves its own promise.
What I don't get still is the callback function. So the client sends data and a callback function A to the server, and the server can call this function A in it's own callback. Function A will NOT be executed on client side, is this understanding correct? If so, then why is there resolve and reject in callback function A?
No. That's not what's happening. The client specifies a callback function to socket.io when it calls the .emit(). That tells the client socket.io library that the client would LIKE to get an acknowledgement for this message. That causes the socket.io library to set an extra bit in the message packet it sends to the server to TELL the server that the client wants an acknowledgement. No callback is sent to the server, only a request for acknowledgement bit. Then, when the server receives the message, it can call a server-side socket.io callback to say "yes, I acknowledge that I got this message" and it can also send a response. When the server calls that socket.io callback, socket.io will send an acknowledgement packet (with a matching message ID embedded in it) to the client. The socket.io client library will see the incoming acknowledgement packet, find the message ID in that packet and any server response data, find the client-side acknowledgement callback that goes with message ID and call it. The client will see its function getting called which tells it that the server did receive its message. Then, in your client code, that callback will resolve the promise.
Background Info
I have a new project I'm working on that will provide multiple different (optional) packages that can be installed, all of which are in addition to the core package (only manual package). The other packages just interact with the core.
The project is just meant to keep track of lists of data (not very specific, I know, but these details aren't needed). The add-on packages determine HOW the lists of data are interacted with. The core package just consists of all the main JS functionality and database models, and authentication. The other packages tie into those.
Lets say you want to just have it as a standard web page, you can install the webui package, which will tie into the core, and create a web app for it
If you want to create an API, you can install the restapi package, which creates the RESTful interface; You can also install the spaui package which will interact with the RESTful interface, which gets the data from the core
These addon packages I will call "facade" packages. All you really need to extrapolate from the above is that the core is a separate package from the facade packages, and it handles the core functionality (Database stuff, authentication, authorization, etc)
Problem
The core can use promises or callbacks, and it returns exceptions for failures, then whatever facade package is used to interact with the core will handle the exceptions/errors (showing an HTTP error page, returning a RESTful error result, etc).
Since the package that handles the errors is different than the package that returns the errors, there needs to be a systematic way of knowing what type of error was returned, so it can be dealt with properly (EG: The webui/restui packages should know if it needs to show a HTTP 500, a HTTP 403, HTTP 409, etc). Obviously of the core just returns new Error('Something broke'), then the facade packages don't really know what type of error it is, unless they have the text saved somewhere and can match it up with an error code.
Question
Whats the best way to handle this? I haven't been able to find anything that accomplishes this exactly how I want..
I eventually started working on my own attempt.. (below)
My Possible Solution (If this is sufficient, just confirm)
I created a new AppError exception type, and instead of returning AppError exceptions with simple strings, you provide an error code which will associate that exception with the error message, error type, etc.
Here is an example usage of the AppError exception:
exports.createThing = ( name, data ) => {
return new Promise( ( res, rej ) => {
if( doesItExist( name ) )
return rej( new AppError( 'document.create.duplicateName' ) )
// Other stuff...
})
}
Now inside the AppError exception method, it takes the code and looks inside a list of exceptions (the code should be the key inside an object of exception data).
Heres an example of what the exception data object for the above exception would contain:
module.exports = {
'document.create.duplicateName': {
type: 'DocumentConflict',
message: 'Failed to create new document',
detail: 'The document name specified already exists, try another one'
}
}
Example Usage: Lets say we try to execute createThing with an already existing name (From within the webui package):
CorePackage.createThing( 'foobar', 'some data' )
.catch( err => {
/*
The err is now an instance of AppError
err.type -> DocumentConflict
err.message -> Failed to create new document
err.detail -> The document name specified already exists, try another one
*/
})
From here, it's as simple as associating the err.type value with a suitable HTTP error code! (which would probably be HTTP 409 Conflict). Obviously these associations can be kept in an object, making it easy to just retrieve the correct error code for any of the error type values returned. Then the text for the error code is right there in err.message and err.detail
This also makes it easy to introduce some type of locale into the application, as the error, as all that needs to be done is to edit the exception data object.
End of post
So if you think my solution above is a sufficient one, and you cant think of any problems, then please say so. Id like to know if it is or if it isn't. Even if you can't think of a proper solution, but you just know the one I created wont work, share that as well.
If you have an alternative solution, then that would work just as well!
Thanks
I think there are two basic ways to approach this:
code property: Create a new \Error object and assign the code property with information about the error. For example:
var err = new Error('Message');
err.code = "DocumentConflict";
Custom error objects. You could have a seperate Error object per error type that you have. For example, rather than having just AppError, you can have DocumentConflict error.
For projects where I am creating a RESTful API, I like to think in terms of error codes. For most projects, the endpoints will return one of the following codes:
400 (Bad Request)
401 (Credentials Error)
403 (Forbidden)
404 (Not Found).
500 (Internal Server Error).
These then become 'standard' types of Error that I pass around the application. A normal Error object is interpretated as an internal server error, so this will always pass 500 to the endpoint.
For example,
CredentialsError = function (message) {
Error.call(this, arguments);
Error.captureStackTrace(this, this.constructor);
this.message = message;
};
util.inherits(CredentialsError, Error);
CredentialsError.prototype.name = "CredentialsError";
And then just return/throw a new CredentialsError("Invalid password") object as necessary. To check the type of object, you can use instanceof. With Express, for example, you can have an error handler similar to the following:
app.use(function(err, req, res, next) {
var status;
if (err instanceof error.FieldError) {
status = 400;
} else if (err instanceof error.CredentialsError) {
status = 401;
/* etc */
} else {
status = 500;
}
if (status !== 500) {
res.status(status).send(JSON.stringify(
err,
null,
4
));
} else {
// for 500, do not output the error!
console.error(err.stack);
res.status(500).send({
message: "Internal Server Error"
});
}
});
It is also worth noting that you can defined your custom error object constructors to take more than just strings. For example, you can pass objects into a BadRequestError constructor to provide field-level error detail.
Now, in most cases, you can just propagate the errors and the response to the endpoint will make sense. However, there are cases where you want to transmute the type of error. For example, if you have a login endpoint, you might do a request to findUserByEmailAddress(). This could return a NotFoundError object, but you want to capture this in the signIn() function and transmute it to a CredentialsError.