Reputation: 61041
Let's say I am writing a web app with a Server and a Client.
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.
Upvotes: 27
Views: 19808
Reputation: 8847
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.
Upvotes: 8
Reputation: 8534
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);
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
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).
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)
.
Upvotes: 11
Reputation: 821
How about using reverse proxy like Nginx
?
You can easily bind two node apps in one port, and there's many other advantages.
Upvotes: 4