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.
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.
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.
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.
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)
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With