Building a Node.js server that parses the URI - javascript

I am learning node and have put together a working server and framework. After talking with some more experienced developers, it seems that Im doing unnecessary work to route the URI. Evidently the http module has all of those features build in already, although I am not sure what they are or how to apply them.
Here is my current server.js
// SERVER
var http = require('http');
var url = require('url');
var fs = require('fs');
var lib = require('scripts'); // some custom libraries I use
var path = require('path');
var NodeSession = require("node-session");
var _SESSION = new NodeSession({secret: 'Q3UBzdH9GEfiRCTKbi5MTPyChpzXLsTD'});
// uri slug -> controller file
var routes = {
// Dependencies
'/style' : 'control_style', // CSS stylesheets
'/fonts' : 'control_fonts', // Fonts
'/scripts' : 'control_scripts', // JS library and controls
'/public' : 'control_public', // Public Resources
// Page Routes
'/' : 'control_home', // root path
'/login' : 'control_login', // login path
'/logout' : 'control_logout', // logout path
'/admin' : 'control_admin', // admin panel path
'/test' : 'control_test', // test page
'/upload' : 'control_upload', // upload test page
};
// Main Loop
function start(){
var port = process.env.PORT || 8080;
http.createServer(function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
_SESSION.startSession(request, response, function(){
lib._post(request, function(_POST, form, files){ // grabs and parses post data
// file system objects that may be needed in controllers
var _SYSTEM = {fs, form, files, path, pathname};
// get the respective controller module
var module = routes[pathname];
// hack for uri's referencing public directory
if(pathname.includes('/public')) module = routes['/public'];
if(module!=null) { // load the respective controller
console.log("controller: "+module+".js");
var controller = require("../controller/"+module);
controller.data(response, request, _SYSTEM, _POST, _SESSION);
} else { // Not Found
console.log(pathname+' => Not Found');
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Path not found");
response.end();
}
});
});
}).listen(port);
console.log("Server has started.");
}
exports.start = start;
Basically, Im matching each URI to a key in the "routes" object and loading the respective controller. (Normally, I would extend this functionality using Regex, but just keeping it simple for now).
Does the Node "http" module (or any other module) already have built-in the functions to parse URI such that I dont have to use a custom built router?
Please note: I am using native node and trying to avoid Express.js for this particular exercise

Note: This answer was written before this additional requirement was added to the answer: "I am using native node and trying to avoid using Express for this particular exercise"
There are a lot of Node modules that can be used to simplify what you do here.
For example you can use Express:
var app = require('express')();
app.get('/path1', path1Handler);
app.get('/path2', path2Handler);
app.use('/static', express.static(pathToStaticFiles));
app.listen(3333);
etc.
See: http://expressjs.com/

Related

How to separate the code into separate files? [duplicate]

I'm developing my first Node.js App with Socket.IO and everything is fine but now the app is slowly getting bigger and I'd like to divide the app-code into different files for better maintenance.
For example I'm defining all my mongoose schemas and the routings in the main file. Underneath are all the functions for the socket.IO connection. But now I want to have an extra file for the schemas, an extra file for routing and one for the functions.
Of course, I'm aware of the possibility to write my own module or load a file with require. That just does not make sense for me, because I can't work with the vars like app, io or db without making them global. And if I pass them to a function in my module, I can't change them. What am I missing? I'd like to see an example how this is done in practice without using global vars..
It sounds like you have a highly coupled application; it's difficult for you to split out your code into modules because pieces of the application that should not depend on each other do. Looking into the principles of OO design may help out here.
For example, if you were to split your dataabse logic out of the main application, you should be able to do so, as the database logic should not depend on app or io--it should be able to work on its own, and you require it into other pieces of your application to use it.
Here's a fairly basic example--it's more pseudocode than actual code, as the point is to demonstrate modularity by example, not to write a working application. It's also only one of many, many ways you may decide to structure your application.
// =============================
// db.js
var mongoose = require('mongoose');
mongoose.connect(/* ... */);
module.exports = {
User: require('./models/user');
OtherModel: require('./models/other_model');
};
// =============================
// models/user.js (similar for models/other_model.js)
var mongoose = require('mongoose');
var User = new mongoose.Schema({ /* ... */ });
module.exports = mongoose.model('User', User);
// =============================
// routes.js
var db = require('./db');
var User = db.User;
var OtherModel = db.OtherModel;
// This module exports a function, which we call call with
// our Express application and Socket.IO server as arguments
// so that we can access them if we need them.
module.exports = function(app, io) {
app.get('/', function(req, res) {
// home page logic ...
});
app.post('/users/:id', function(req, res) {
User.create(/* ... */);
});
};
// =============================
// realtime.js
var db = require('./db');
var OtherModel = db.OtherModel;
module.exports = function(io) {
io.sockets.on('connection', function(socket) {
socket.on('someEvent', function() {
OtherModel.find(/* ... */);
});
});
};
// =============================
// application.js
var express = require('express');
var sio = require('socket.io');
var routes = require('./routes');
var realtime = require('./realtime');
var app = express();
var server = http.createServer(app);
var io = sio.listen(server);
// all your app.use() and app.configure() here...
// Load in the routes by calling the function we
// exported in routes.js
routes(app, io);
// Similarly with our realtime module.
realtime(io);
server.listen(8080);
This was all written off the top of my head with minimal checking of the documentation for various APIs, but I hope it plants the seeds of how you might go about extracting modules from your application.

How to host an app on node server?

I have a simple html file that refers to a CSS stylesheet and a javascript file. I'm supposed to host it on a node server. I Googled the procedure and surmised that I was supposed to include the files like so,
var http = require('http');
var fs = require('fs');
var index = fs.readFileSync('index.html');
console.log(index);
var javaScriptFile = require('some/javascript/here');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'html'});
res.end(index);
}).listen(10024);
When I run the code, my HTML is rendered but my file treeStructServer.js is shown as an exact copy of the HTML, which causes an error. Any idea why that's happening? I'm out of my depth.
With your current node script you just return the HTML file on every request, no matter what the URL is. You could instead use the node-static module. Install it by running npm install node-static and then replace your Node code with the following:
var static = require('node-static');
//
// Create a node-static server instance to serve the current folder
//
var file = new static.Server('./');
require('http').createServer(function (request, response) {
request.addListener('end', function () {
//
// Serve files!
//
file.serve(request, response);
}).resume();
}).listen(10024);

Node.js module export

How can I export these variables so I can later use them on different js files?
The following example works well with just 1 variable
var app = module.exports = express();
But I want to pass more variables so I did this
var app = express();
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : ''
});
module.exports.app = app;
module.exports.connection = connection;
with no success
This is because on the first, your module is the app. I mean that if you have B like that in the first case:
app = require('A') // = express()
whereas on the second time it is :
app = require('A') // = {app: express(), connection: connection}
The answer is in the Node.js Modules documentation:
You either assign the value that you want to export to the module.exports property, or you assign an object to it.
mymodule.js:
var app = module.exports = express();
Or:
var app = express();
module.exports = {
"app": app,
"otherproperties": "you want to export"
}
To require a module that you made yourself and didn't place node_modules directory, you can either supply an absolute path or a relative path.
Calling module:
var app = require("/home/user/myapp/mymodule.js"); // absolute path
app; // access returned value of express() function, created in *mymodule.js*
Or:
var app = require("./mymodule.js"); // path relative to the calling module
// In this case the calling module is in the same directory as *mymodule.js*
app.app; // access returned value of express() function, created in *mymodule.js*
Addendum: Even though the modules library/module has been locked, I really recommend reading the documentation. It's very much possible to read the entire documentation in two evenings while looking up network terminology that you're not familiar with. It'll save you a lot of time in the short term!

How to write a node express app that serves most local files, but reroutes some to another domain?

I'm working on a small webapp that normally is built with a relatively complex process and then deployed to WebLogic.
However, the portion I'm working on is using AngularJS, and is all HTML and Javascript. It normally makes ajax calls into another webapp on the same domain. To shorten my development cycle, I'd like to avoid a build process and just reload the browser page.
I think I can do this with "node express", but the details escape me. I've managed to define a very simple app that just serves local files, but now I have to figure out how to detect some of those paths as matching an expression, and reroute those requests to a request to an external domain.
So, if it gets a request for "/diag/stuff.html", "/foo/thing.html", or just "/index.html", it will send back the file matching the same path. However, if the path matches "/fooService/.*", then I have to send back the response from a GET to the same path, but on a different host and port.
This is my trivial app so far:
var express = require('express');
var app = express();
app.use("/", express.static(__dirname));
app.listen(8000);
Update:
I like the proxy idea, so I did a local install of "http-proxy" (I forgot and first did a global install) then changed the script to this:
var express = require('express');
var app = express();
var httpProxy = require('http-proxy');
var proxy = new httpProxy.RoutingProxy();
app.use("/", express.static(__dirname));
app.get('/FooService/*', function(req, res) {
"use strict";
return proxy.proxyRequest(req, res, {
host: "foohost.net",
port: 80
});
});
app.listen(8000);
This fails with:
<path>\server.js:4
var proxy = new httpProxy.RoutingProxy();
^
TypeError: undefined is not a function
at Object.<anonymous> (<path>\server.js:4:13)
What might be wrong here?
Update:
Would it be useful to see the contents of "console.log(httpProxy)" after that "require"?:
function ProxyServer(options) {
EE3.call(this);
this.web = this.proxyRequest = createRightProxy('web')(options);
this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options);
this.options = options;
this.webPasses = Object.keys(web).map(function(pass) {
return web[pass];
});
this.wsPasses = Object.keys(ws).map(function(pass) {
return ws[pass];
});
this.on('error', this.onError.bind(this));
}
Does that provide a clue for why "new httpProxy.RoutingProxy()" says it's undefined?
You can use http-proxy and forward requests to different host. To install http-proxy you need to run sudo npm install http-proxy. Code that will handle proxy will look like that:
httpProxy = require('http-proxy');
proxy = new httpProxy.RoutingProxy();
(...)
app.get('/fooService/*', function (request, response) {
"use strict";
return proxy.proxyRequest(request, response, {
host : externalHost,
port : 80
});
});
UPDATE
Above code is working for http-proxy ~0.10.x. Since then lot of things had changed in library. Below you can find example for new version (at time of writing ~1.0.2):
var httpProxy = require('http-proxy'),
proxy = httpProxy.createProxyServer({});
(...)
app.get('/fooService/*', function (request, response) {
"use strict";
return proxy.web(request, response, {
target: 'http://fooservice.com'
});
});
If redirects meet your need, then that's the easiest solution:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.use(app.router);
app.get('/fooService/*', function(req, res){
res.redirect(302, 'http://otherdomain.com:2222' + req.path);
});
app.listen(8000);
Note that it's generally considered good practice to use a subdirectory for your static files (like public above). Otherwise you could view your app file itself and anything else you keep in your application root!

rendering jade files in expressjs

I have a basic expressjs app (using jade), but I am having trouble rendering basic Jade files. When I get a request, i parse the url for the pathname and use the handle object to route the request as follows:
index.js
var requestHandlers = require('./requestHandlers');
var handle = {};
handle['/'] = requestHandlers.start;
handle['/download'] = requestHandlers.download
requestHandlers.js
function start(res) {
console.log("request handler for start called");
res.render('home', {title: 'express'});
}
function download(res) {
res.render('download', {title: 'download'})
res.end();
}
exports.start = start;
exports.download = download;
home.jade
h1= title
p Welcome to #{title}
I am using Jade as my templating engine, and have configured the server in a seperate server.js file. When i request either of the pages, the title displays correctly on my browser tab, but the page doesn't display, it just keeps loading. Weirdly, when I cancel the request the page displays. It's as if everything works but nothing tells the process to end?
I am relatively new to node so excuse my naiveté on any of the above. Let me know if there are any questions I can clear up.
I'm not 100% positive why your code isn't killing the TCP connection as needed to prevent your browser from timing out, but I can provide a solution that is friendly towards Express conventions that should solve your issues and maintain code readability, maintainability, and separation.
./app.js (your main server script)
var express = require('express'),
app = express.createServer(),
routes = require('./routes');
app.configure(function () {
// Render .jade files found in the ./views folder
// I believe you are already doing this
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
// Use the built-in Express router
// This kills the "handle" method you had implemented
app.use(app.router);
// Client-side assets will be served from a separate ./public folder
// i.e. http://yourhost.com/js/main.js will link to ./public/js/main.js
app.use(express.static(__dirname + '/public'));
});
// Initialize routes
routes.init(app);
./routes/index.js
exports.init = function (app) {
app.get('/', function (req, res) {
console.log("request handler for start called");
// Render __dirname/views/home.jade
res.render('home', {title: 'express'});
});
app.get('/download', function (req, res) {
// Render __dirname/views/download.jade
res.render('download', {title: 'download'})
});
});
The above prevents you from needing to parse the URL parameters by yourself. Also you can define more readable and powerful request handlers (i.e. app.post for POST methods). You are now enabled to more easily tie in things like the Express-Resource module if you decide to build a REST API.
If you need more powerful mappings you can use regular expressions in the first parameter of app.[get/post/put/del] to filter for specific paths instead.

Categories