When I undertook my first "big" web application in 2009, I did it in Django. The project was fairly ambitious not just because it was a web application, but it was also my first foray into cryptographic protocols. I was trying to design a system where the client and the server didn't necessarily trust each other.
Note As an exercise for any programmer who hasn't delved into cryptography, I have a suggestion. Pick an application you've written and look at a communication, then ask: "What if this piece didn't trust that piece?" We often take a lot for granted, and seeing things in this new light really helps you appreciate how nuanced a cryptographic protocol can be. I'd suggest reading through Applied Cryptography if you haven't--I did before starting my project, and it was very accessible and an interesting read.
This led me to go around in a rant to people which sounded something like this:
But in 2012 I went to a virtualization conference hosted by VMWare. I showed the demo of my Python-server-based system to one of the guys from Cloud Foundry. He thought the project was cool, but mentioned they didn't have Django support, and suggested I port it to Node.JS. There were representatives from MongoDB there too, which was one of the facilities that came on Cloud Foundry. It looked like it wouldn't be too hard to switch from Django/MySQL to Node/MongoDB for the server component, and things might simplify a bit.
So I decided maybe the time had come to grit my teeth and embrace my prophecy. The result is what is now published as Blackhighlighter.
Well...I was half right
Node.JS has taken off in popularity, which shows that I was onto something. Yet in my case I wanted to just share some simple routines between the browser and the server. This turned out to not be straightforward at all, despite seeming like one of the most basic things you should be able to do. In fact, there aren't that many people who really care about this.
The "module system" in Node.JS is pretty reasonable. It's managed by a tool called "NPM", and if your node program has dependencies you just put them into a
package.jsonfile with a version indicator you want. You can use wildcards too, so asking for
0.10.*will get you the latest release of subversion 10 of something.
The core of the problem is that when web browsers were released, they just lumped everything global into a single object, called window. This resulted in some workarounds that are known as the "module pattern", and you can read it explained and what's considered "best practices". But when Node.JS was released, if you declare a file-global element, it isn't seen by other files in the project.
In the browser, a global declaration implicitly becomes a member of the
windowobject. Node's parallel to
process(from which you can get things like environment variables, and lots of other things). But a key difference is that an unqualified global declaration does not implicitly become a member of
process; it is only available inside that module unless you explicitly stick it into an
Meet RequireJS and Browserify
Node.js comes with a loading function called "require" which synchronously loads script code from the local disk. It has some amount of convention so you don't have to necessarily specify the full path to a module, and you also omit the ".js" extension. As this is the only way, it means that if you are looking to share code between client and server code with node.js, you need some kind of adapter.
The most thoroughly-considered adapter is named RequireJS. It introduces an inclusive standard that means you write your modules in a way that isn't compatible with either require or the <script> method (facepalm).
Long story short: you can use
requireordinarily in the node modules, unless that module is intended to be shared between the browser and server. If you want to include one of those shared modules, then use the the keyword requirejs to import it.
Take three clicks in any direction, and you will land in a passionate arguments from people who say that the better alternative is to introduce a compilation step. Basically throw asynchronous loading out the window, and glom everything needed by your page into one file to blob over the network. Your node program won't care as the files are local, and the browser would probably rather just have everything sent as a minified blob anyway. How many pages do anything meaningful when they're "half loaded", anyhow? A popular choice in this area is called Browserify
But if you value things like...oh, debugging, say...or if you're explicitly trying to convince people your code has nothing to hide so they trust it, you may not want to obfuscate like that. So I went with RequireJS. Having thought about the art of software engineering a lot, I cry a little inside when I think that this is what young programmers are reading up on today and consider it "learning something". HOWEVER--it works, is documented and supported, and there are enough people using it that someone will presumably write the "how to change your code from requirejs to the real answer".
jQuery, Underscore.JS, or native JS in shared code?
BlackHighlighter is a jQuery plug-in which needs to share code between the client and the Node.JS server. A key constraint on it is that I did not want to include jQuery on the server.
There actually is an NPM jQuery package, and you can do DOM queries with it. If you mention it people will groan and ask why you would want such a thing, and admonish you not to use it. I see their point--and preferred to use underscore.JS on the server side. Yet I didn't want to depend on underscore AND jQuery on the client. This put me in a bit of a bind.