In my node.js application i used to handle error by domain.
The architecture of the app looks like:
Controllers. Express routing calls controllers methods
Controllers call services and use models
Services call repositories
(Actually it's quite similar to DDD).
So, controllers create domain, and run its actual body in the domain. Services used to throw exceptions if something's going wrong. Also controllers listen for domain errors and process them. Its very comfortable because i dont need to worry about carring an error over all services's method callstack — i just throw an exception and can be sure that it would be caught in controller.
But i have faced a problem connected with using PostgreSQL.
I use node-postgres module and i create pg.Client in separated js file, so pg.Client is like shared for everybody (otherwise, creating pg.Client on each query makes open lots of active connections with postgres).
The problem is that when pg.Client is definded in separated file it's like a global object and it's not included in domain scope created in controllers. So exceptions throwed from pg.Client callbacks are not caught by domain.
I will show simplified way of request processing to make it clear. Let's say user wants to get login by userId:
Somewhere in the beggining pg.Client created
Get request comes to express, express call routing method
Routing calls some of controller's method
Controller creates domain and calls in «domain.run» a service
Service calls a repository
Repository takes pg.Client created ealier, and calls sql query method
Result of sql query method puts in callback (so this callback is called by pg.Client)
And then the callback is processed in service, where we check that we get null instead of user model (because there is no such user in db for given userId) and throw an exception
So that exception is not caught in domain.
Technically, we need to use «add» method of node domain and add pg.Client, but how i said pg.Client is shared, hence it would be added in different concurrent domains.
I will list some code example to make it more clear.
It's simplified method of UserService:
login: function (email) {
var userRepository = new UserRepository();
userRepository.findByEmail(email, function (model) {
if (model == null)
throw new Error('No such user');
});
}
So, that method «login» is called in domain. But it created userRepository, calls findByEmail method which will use shared pg.Client laying outside of domain's scope, and that's why exception will not be caught in domain.
Any ideas how to fix it and put pg.Client in the domain?
I solved the issue.
I create EventEmitter in scope of active domain and listen lets say «onCallback» event.
In callback of query method (which is not connected with the domain, because pg.Client is kinda global and lays out of the domain) i emit «onCallback» of the EventEmitter.
Related
I am using NestJS as a backend framework in NodeJS +16
I am trying to implement:
https://medium.com/#sascha.wolff/advanced-nestjs-how-to-have-access-to-the-current-user-in-every-service-without-request-scope-2586665741f
My idea is to have a #Injectable() service that will have, among other things, methods like:
hasUserSomeStuff(){
const user = UserStorage.get()
if(user) {
// do magic
}
and then pass this service around as it is usually done in NestJS
To avoid passing the request down the rabbit hole, or bubbling up the request scope so every dependency gets instantiated for each request, but also avoiding to use UserStorage everywhere where I need to get the user from the current request and do stuff
I've gone through the docs many times, it is my understanding that node would take care of instantiating a new storage for each async context (in my case each request), but what seems to happen to me is that when I first run my backend, it works just fine, I've got the user from the current request, but once the first async context / promise is completed, I retrieved data for the consumer, and in the next request UserStorage returns a undefined (as doc states it will if you are outside of the same async context, which I guess it is not what happens, as it should be a brand new async context)
However if I debug, what seems to happen is that this UserStorage is called and a new AsyncLocalStorage is instantiated at init, before the app is ready to be used, and then the very first request returns a undefined user.
I am failing to understand what is going on, can anyone help me on this, or any better approach to achieve my goal?
Thanks in advance
My Node.js knowledge is fairly basic. I'm using socket.io to create a server and socket.io-client-swift in an iOS app.
I would like the server to create a dynamic namespace depending on the users selection on the client app. The idea being that all users with a specific selection join the same namespace on the server. This allows messages to be broadcast to all of those users within the namespace.
The problem is that the socket.io documentation and code appears to assume that you will be hardcoding the namespace name in the node.js code and that the namespace will be defined before the client iOS code attempts to join it.
Here's what i would like to occur:
Client requests to join a namespace called 'abc'.
Server receives request.
Something on the server checks the request, figures out there is no current namespace for 'abc' and dynamically allocates one.
Server finishes processing the join request, connecting the socket to the newly created namespace.
It's step #3 that I'm having problems with. I've tried using io.use(function(socket, next){...} to intercept the incoming request, but it appears that occurs after the server has determined whether there is a matching namespace for the request.
Does anyone have any idea how to intercept an incoming namespace request?
I think I figured it out. I added some code to my server.js like this:
// Lets Swizzle in some wrapper code.
//var originalConnection = Client.prototype.connect;
Client.prototype.originalConnect = Client.prototype.connect;
Client.prototype.connect = function(name){
var nsp = io.nsps[name];
if (!nsp) {
io.of(name);
}
this.originalConnect(name);
};
Effectively I swizzled in a wrapper function that does the dynamic namespace creation.
I'm trying to decide between two methods for inserting a new document to a collection from the client using Meteor.js. Call a Server Method or using the db API directly.
So, I can either access the db api directly on the client:
MyCollection.insert(doc)
Or, I can create a new Server Method (under the /server dir):
Meteor.methods({
createNew: function(doc) {
check(doc, etc)
var id = MyCollection.insert(doc);
return project_id;
}
});
And then call it from the client like this:
Meteor.call('createNew', doc, function(error, result){
// Carry on
});
Both work but as far as I can see from testing, I only benefit from latency compensation (the local cache updating and showing on the screen before the server responds) if I hit the db api directly, not if I use a Server Method, so my preference is for doing things this way. But I also get the impression the most secure approach is to use a Method on the server (mainly because Emily Stark gave it as an example in her video here) but then the db api is available on the client no matter what so why would a Server Method be better?
I've seen both approaches taken when reading source code elsewhere so I'm stumped.
Note. In both cases I have suitable Allow/Deny rules in place:
MyCollection.allow({
insert: function(userId, project){
return isAllowedTo.createDoc(userId, doc);
},
update: function(userId, doc){
return isAllowedTo.editDoc(userId, doc);
},
remove: function(userId, doc){
return isAllowedTo.removeDoc(userId, doc);
}
});
In short: Which is recommended and why?
The problem was that I had the method declarations under the /server folder, so they were not available to the client and this broke latency compensation (where the client creates stubs of these methods to simulate the action but in my case could not because it couldn't see them). After moving them out of this folder I am able to use Server Methods in a clean, safe and latency-compensated manner (even with all my Allow/Deny rules set to false - they do nothing and only apply to direct db api access from the client, not server).
In short: don't use the db api on the client or allow/deny rules on the server, forget they ever existed and just write Server Methods, make sure they're accessible to both client and server, and use these for crud instead.
Im working on a socket.io based server/client connection instead of ajax.
Client uses Backbone and I overwritten the Backbone.sync function with one
half assed of my own:
Backbone.sync = function (method, collection, options) {
// use the window.io variable that was attached on init
var socket = window.io.connect('http://localhost:3000');
// emit the collection/model data with standard ajax method names and options
socket.emit(method,{collection:collection.name,url:collection.url});
// create a model in the collection for each frame coming in through that connection
socket.on(collection.url,function(socket_frame){
collection.create(socket_frame['model']);
})
};
Instead of ajax calls I simply emit through socket attached to window.io
global var. Server listens to those emits and based on the model url, I don't want to change that behaviour and I use the default crud method names (read,patch...) inside each emited frame. The logic behind it (its a bit far thought, but who knows) that in case the client doesn't support Websockets I can easily fallback to default jQuery ajax. I attached the orginal Backbone.sync to a var so I can pass the same arguments to it when no websocket is available.
All it that greatness behalves properly and the server answers to the client events. The server emits then each model data as a seperate websocket frames in one connection.
I see the frames in the Network/Websocket filter as one (concurrent/established) connection
and things seems to be working
Currently the function assumes I pass a collection and not a model.
Questions:
Is that approach ok with you?
How can I use the socket.io callbacks on 'success' and 'failure' etc in Backbone the right way so I don't have to call the collection.create function 'by-hand'?
Is it better to establish different concurrent connections for models/collections or use the one already established instead?
I am working with a partner on a project. He has written a lot of code in Node.js+Express, but we've been running into issues with the architecture.
To remedy this, my primary role has been to figure out the best way to architect a Node.js+Express application. I've run into two scenarios, dealing with errors, and I'd like some suggestions.
First, how do I capture top-level exceptions? The last thing I want is for a bug to completely kill the node process. I want to continue serving users in the face of any error.
Secondly, some errors are passed back via callbacks (we're using caolan / async). As part of each route handler, either we render a view (GET), redirect to another route (POST) and we want to redirect to an error screen with a custom error message. How can I make sure to capture this logic in one place?
First, how do I capture top-level exceptions? The last thing I want is for a bug to completely kill the node process. I want to continue serving users in the face of any error.
Edit: I think node's philosophy in general is that any uncaught exceptions should kill the process, and that you should run your node app under some kind of process monitor with appropriate logging facilities. The following advice is regarding any other errors you might encounter in your express route handlers etc.
Express has a general errorHandler, which should capture all thrown errors as well as everything passed as a parameter to next in your routes/middlewares, and respond with 500 Internal Server Error.
Secondly, some errors are passed back via callbacks (we're using caolan / async). As part of each route handler, either we render a view (GET), redirect to another route (POST) and we want to redirect to an error screen with a custom error message. How can I make sure to capture this logic in one place?
You could create a custom handleError, which you call in each callback like so:
async.series(..., function(err, results) {
if(err)
return handleError(req, res, err);
// ...
});
Or you could just pass the errors on with next(err) and implement your custom error handler as described here: http://expressjs.com/guide/error-handling.html
Top level exceptions:
You can use the uncaughtException event from process, but it's generally not recommended.
Often applications will go into a corrupted state (eg. you have some state which typically gets set, but the exception caused that not to happen) when an exception is thrown. Then, it will just cause more and more errors from there on onwards.
A recommended approach is to use something like forever to automatically restart the app in case it crashes. This way you will have the application in a sane state even after a crash.
Error handling in express:
You can create a new Error instance and pass it to the next callback in the chain.
Eg.
express.get('/some/url', function(req, res, next) {
//something here
if(error) {
next(new Error('blah blah'));
}
});
To handle the error from here on onwards, you can set an error handler. See express docs on error handling
Checkout the excellent log-handling module Winston: https://github.com/flatiron/winston
It allows you to configure exception handling in a manner that will not only log it, but will allow the process to continue. And, since these would obviously be serious issues, you can even configure Winston to send out emails on specific event types (like exceptions).