Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating Node.js server and client

Let's say I am writing a web app with a Server and a Client.

  • The server functions as an API, and uses express framework.
  • The client is just a node-static app that serves static javascript/html files.

I want to be able to deploy them separately, independently of each other - or both at the same time.

Here is how I envision the directory structure:

 /my-app
     app.js
     /server
         server.js
     /client
         client.js

I would like to be able to run this in 3 different ways:

  1. Run just the server (API) on some port (say 3000):

    my-app/server> node server.js
    ...Server listening on localhost:3000/api
    
  2. Run just the client (i.e. serve static files from /client directory):

    my-app/client> node client.js
    ...Server listening on localhost:4000/client
    
  3. Run both the server and the client, on the same port (by single node.js instance):

    my-app> node app.js
    ...Server listening on localhost:5000
    

Is this possibe in node and what is the proper way to configure it?

I started as follows:

/////////////
// server.js
/////////////
// Run the server if this file is run as script
if(module.parent){
   app.listen("3000/client")  
}

/////////////
// client.js
/////////////
var static = require('node-static');
var file = new(static.Server)('.');
var app = require('http').createServer(function (request, response) {
  request.addListener('end', function () {
    file.serve(request, response);
  });
});
if(module.parent){
   app.listen("4000/client");
}

/////////////
// app.js
/////////////
server = require("server/server.js")
server.app.listen("5000/api")

client = require("client/client.js")
client.app.listen("5000/client")  <--- ?????

I am not sure how to hook up both client and server inside my app.js so that they are both served from the same port/process/thread etc...

NOTE: Excuse the code, it is not tested and probably incorrect. I am new to node.js

Any tips appreciated.

like image 713
Andriy Drozdyuk Avatar asked Jan 14 '13 00:01

Andriy Drozdyuk


People also ask

Why should you separate express app and server in Nodejs?

Faster testing execution. Getting wider coverage metrics of the code. Allows deploying the same API under flexible and different network conditions. Better separation of concerns and cleaner code.

Is node js server or client?

Node. js is a server-side JavaScript run-time environment. It's open-source, including Google's V8 engine, libuv for cross-platform compatibility, and a core library.

Why we use express in node JS?

Express is a node js web application framework that provides broad features for building web and mobile applications. It is used to build a single page, multipage, and hybrid web application. It's a layer built on the top of the Node js that helps manage servers and routes.


2 Answers

You can instantiate a connect (the guts of express) server instance when starting both the server and the client from the same script and have it route the requests to node-static when the url starts with public and to connect otherwise.

Something like

var connect = require('connect'); server = connect.createServer(); server.use('/public', client.handler_function); server.use(server.express_handler_function); 

should do. You'll need to expose the function(request, response) in client.js so that it can be referenced through client.handler_function and do the same for the express app (refer to the documentation).

For example, esposing the function in client.js would involve something like:

var static = require('node-static'); var file = new(static.Server)('.'); var handler = function (request, response) {   request.addListener('end', function () {     file.serve(request, response);   }); }; var app = require('http').createServer(handler); if(module.parent){    app.listen(4000);    console.log("Server (static) listening on 4000") } module.exports = { handler_function: handler }; 

So that you can get to handler by doing:

var client = require('client'); // this returns the object you've set to `module.exports` in `client.js` [...] server.use('/public', client.handler_function); 

Other approaches

What I've detailed above seems to be the closest to what you want (based on the clarification in your last edit). There are other options, though:

  • keep static and express-generated urls based at the site root, such as example.com/a_statically_served_script.js and example.com/api_endpoint; serving a static file is attempted first, if one cannot be found you'll dispatch the request to the express-based app

  • use the app.js script to start both servers on different ports (or unix domain sockets) and use node-proxy (or something similar, or even nginx/apache as a reverse proxy) in front of them

Same root

For the first approach you need to add an error handler to file.serve such as

file.serve(request, response, function(e, res) {     if (e && (e.status == 404)) {         // if the file wasn't found         if (next) {             next(request, response);         }     } } 

next should be a variable in the client.js script that is not set when the script is run directly but it is when the script is required (have a look at the documentation for how modules and exports in node work) - when set, next refers to a function that takes (req, res) and feeds them to express (have a look at the express docs on how to do this).

Remarks

Keep in mind this isn't an exhaustive answer: it's just a bunch of pointers on what documentation to look up and what techniques you could use to solve the problems.

Something worth remembering is that more often than not in node a request handler is represented and implemented by a function(request, response). This idiom is extended in connect/express to funciton(request, response, next): here next represents the next avaliable handler (of the form function(request, response)) in the chain of handlers mounted to the server through server.use(handler).

like image 141
Utaal Avatar answered Sep 28 '22 21:09

Utaal


I had similar requirements to the OP, but didn't need to execute subsections separately... I only wanted separate folders as I don't like nesting client code within the server's code folder. Technically it's impossible to avoid doing so, so instead I created a level of abstraction in the filesystem to ease my troubled soul. Here is the root of my project:

-client
-server
-node_modules
-app.js

client contains everything that public usually would (all client-side files to be served), i.e. at least index.html.

server may or may not be a Node module (in my case it didn't have to be). Herein lies your app-specific server code. You can re-require the same files as in your underlying app.js, as it won't make any difference and will work just fine.

node_modules is obviously required by Node, and is best left at the root of the project, since that way any requires in server can search upward through the project folder hierarchy to find this common repository of modules at the root.

app.js simply contains the following:

var path = require('path');
var express = require('express');
app = express();
require('./server/server.js');

app.use(express.directory(path.join(__dirname, '/'))); //optional, directory browsing
app.use(express.static(path.join(__dirname, '/')));
app.use(express.errorHandler());

app.listen(process.env.PORT || 5000);

...So as you can see, there is minimal code in the root script. All really important, application-specific

Note that the variable app is not declared using var. By avoiding var, we make it global and allows it to be accessed within server.js without further ado.

So you would do node app to get this running, if that wasn't already clear.

like image 40
Engineer Avatar answered Sep 28 '22 20:09

Engineer