Separating Node.js server and client
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
andexample.com/api_endpoint
; serving a static file is attempted first, if one cannot be found you'll dispatch the request to the express-based appuse 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 require
s 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.
Related videos on Youtube
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, 2022Comments
-
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:
-
Run just the server (API) on some port (say 3000):
my-app/server> node server.js ...Server listening on localhost:3000/api
-
Run just the client (i.e. serve static files from /client directory):
my-app/client> node client.js ...Server listening on localhost:4000/client
-
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 over 11 yearsIn 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 over 11 yearsYes! You are understanding me exactly.
-
Andriy Drozdyuk over 11 yearsSorry, 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
andlocalhost: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 over 11 yearsThanks - 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 over 11 yearsThanks, but I am running in Heroku. And I want to keep the number of node apps to a minimum - just 1.
-
Utaal over 11 yearsI'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 over 6 yearsHow did you configure the
package.json
file for this dir/file structure? -
Engineer over 6 years@InfZero Wish I could help but 4 years was a long time ago.