Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Global scope for every request in NodeJS Express

I have a basic express server that needs to store some global variables during each request handling. More in depth, request handling involves many operation that need to be stored in a variable such as global.transaction[]

Of course if I use the global scope, every connection will share information of its transaction and I need a global scope because I need to access the transaction array from many other modules, during my execution.

Any suggestion on this problem? I feel like is something very trivial but I'm looking for complicated solutions :)

Many thanks!

UPDATE This is a case scenario, to be more clear.

On every request I have 3 modules (ModuleA, ModuleB, ModuleC) which read the content of 10 random files in one directory. I want to keep track of the list of file names read by every request, and send back with res.write the list.

So ModuleA/B/C need to access a sort of global variable but the lists of request_1, request_2, request_3 etc... don't have to mix up.

like image 477
Leonardo Rossi Avatar asked Nov 12 '13 09:11

Leonardo Rossi


1 Answers

Here is my suggestion avoid global state like fire.

  • It's the number one maintenance problem in Node servers from my experience.
  • It makes your code not composable and harder to reuse.
  • It creates implicit dependencies in your code - you're never sure which piece depends on which and it's not easy to verify.

You want the parts of code that each piece of an application uses to be as explicit as possible. It's a huge issue.

The issue

We want to synchronize state across multiple requests and act accordingly. This is a very big problem in writing software - some say even the biggest. The importance of the way objects in the application communicate can not be overestimated.

Some solutions

There are several ways to accomplish sharing state across requests or server wide in a Node server. It depends on what you want to do. Here are the two most common imo.

  1. I want to observe what the requests do.
  2. I want one request to do things based on what another request did.

1. I want to observe what the requests do

Again, there are many ways to do this. Here are the two I see most.

Using an event emitter

This way requests emit events. The application reads events the requests fire and learns about them accordingly. The application itself could be an event emitter you can observe from the outside.

You can do something like:

request.emit("Client did something silly", theSillyThing);

And then listen to it from the outside if you choose to.

Using an observer pattern

This is like an event emitter but reversed. You keep a list of dependencies on the request and call a handler method on them yourself when something interesting happens on the request.

Personally, I usually prefer an event emitter because I think they usually solve the case better.

2. I want one request to do things based on what another request did.

This is a lot tricker than just listening. again, there are several approaches here. What they have in common is that we put the sharing in a service

Instead of having global state - each request gets access to a service - for example when you read a file you notify the service and when you want a list of read files - you ask the service. Everything is explicit in the dependency.

The service is not global, only dependencies of it. For example, it can coordinate resources and the data, being some form of Repository).

Nice theory! Now what about my use case?

Here are two options for what I would do in your case. It's far from the only solution.

First option:

  • Each of the modules are an event emitter, whenever they read a file they emit an event.
  • A service listens to all their events and keeps count.
  • Requests have access to that service explicitly and can query it for a list of files.
  • Requests perform writes through the modules themselves and not the added service.

Second option:

  • Create a service that owns a copy of module1, module2 and module3. (composition)
  • The service delegates actions to the modules based on what is required from it.
  • The service keeps the list of files accessed since the requests were made through it.
  • The request stops using the modules directly - uses the service instead.

Both these approaches have advantages and disadvantages. A more complicated solution might be required (those two are in practice pretty simple to do) where the services are abstracted further but I think this is a good start.

like image 71
Benjamin Gruenbaum Avatar answered Oct 16 '22 21:10

Benjamin Gruenbaum