Separating Node.js server and client

18,438

Solution 1

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).

Solution 2

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.

Solution 3

How about using reverse proxy like Nginx?

You can easily bind two node apps in one port, and there's many other advantages.

http://wiki.nginx.org/Main

Share:
18,438

Related videos on Youtube

Andriy Drozdyuk
Author by

Andriy Drozdyuk

I like AI and in particular Reinforcement Learning. I used to like Erlang, Scala, python, Akka and Zeromq! Did my undergrad in Computer Science at University of Toronto. Did my masters in Data Mining at University of New Brunswick. Working as a programmer at NRC and doing PhD part time at Carleton University.

Updated on September 14, 2022

Comments

  • Andriy Drozdyuk
    Andriy Drozdyuk over 1 year

    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.

    • Utaal
      Utaal over 11 years
      In my answer I'm assuming that by "client" you refer to the html/css/js/... that should be server statically to the browser. Am I getting this right?
    • Andriy Drozdyuk
      Andriy Drozdyuk over 11 years
      Yes! You are understanding me exactly.
  • Andriy Drozdyuk
    Andriy Drozdyuk over 11 years
    Sorry, I guess I completely forgot to mention that I didn't expect both client and server to be served at the same url. Sorry that would be silly of course! I meant something like localhost:5000/server and localhost:5000/client ... Basically as you indicated (I've update the question to reflect the urls that would serve the content in each case, thanks!)
  • Andriy Drozdyuk
    Andriy Drozdyuk over 11 years
    Thanks - great answer. I'm still a bit unclear as to what you mean by "expose the function(request, response)" - perhaps you could point me to the related section in the docs somewhere or let me know what to search for? Cheers!
  • Andriy Drozdyuk
    Andriy Drozdyuk over 11 years
    Thanks, but I am running in Heroku. And I want to keep the number of node apps to a minimum - just 1.
  • Utaal
    Utaal over 11 years
    I'm happy this helps! I've extended the answer with an example. Let me know if it's not clear. Search for 'creating a module in nodejs' for more info on how modules work. Cheers!
  • InfZero
    InfZero over 6 years
    How did you configure the package.json file for this dir/file structure?
  • Engineer
    Engineer over 6 years
    @InfZero Wish I could help but 4 years was a long time ago.