Falsy Values JavaScript conference
Node.js workshop starting. Poll of people participating… most write mainly PHP, Java, Python, Ruby. But everybody also writes JavaScript.
2% of all job postings, counting even non-programming ones, mention JavaScript
Server-side JavaScript is not new, Rhino and others have been there since 90s. So, why is Node now so hot?
JS community has become more professional
Runtimes are way faster now: V8, SpiderMonkey, … No other language has such competition between runtimes
There is now also Node.js running on top of SpiderMonkey.
Via Twitter: Node.js style guide http://nodeguide.com/style.html
Node.js is very young platform. V8 is from September 2008, and Node.js was started in February 2009. So only half a year after the engine it runs on.
Installing latest dev version of node.js is easy: configure, make, make install
node-inspector seems like a very nice way to debug node.js
First exercise:
- Create the basic Node.js http server
- Modify it to return some other text
- Make it return HTML
- Return the user-agent
- Return different text for at least two different browsers
My version, in CoffeeScript:
http = require 'http'
browserSpecific = (agent) ->
if agent.indexOf("Chromium") isnt -1
return "an Agent of Google"
"something I don't recognize"
server = http.createServer (req, res) ->
res.writeHead 200,
'Content-Type': 'text/html'
'X-Powered-By': 'Coffee'
res.end "<h1>I'm learning Node</h1>
<p>And you're #{browserSpecific(req.headers['user-agent'])}"
server.listen 8003, '127.0.0.1'
HTTP client exercise:
- Fetch the nytimes.com and output to console
- Create a web server
- Create an HTTP client in the same file
- POST data to your server
- Output the POST data
Solution:
http = require "http"
options =
host: "127.0.0.1"
port: 8003
path: "/"
method: "POST"
server = http.createServer (req, res) ->
console.log "Got request"
req.setEncoding "utf-8"
req.on "data", (content) ->
console.log "DATA: #{content}"
res.writeHead 200,
'Content-Type': 'text/plain'
res.end "Hey there"
server.listen options.port, options.host, ->
client = http.request options, (results) ->
results.setEncoding "utf-8"
results.on "data", (content) ->
console.log content
client.write "Foo bar baz"
client.end()
CommonJS module exercise:
- Create CommonJS module “fish”
- Provide functions to swim, mouthbreath, flap around
- Import module into another file and call the methods
fish.coffee:
exports.swim = ->
"swims"
exports.mouthBreath = ->
"doesn't know how to mouthbreath"
exports.flapAround = ->
"happily flaps around"
useFish.coffee:
fish = require("./fish")
console.log "Fish #{fish.swim()}"
console.log "Fish #{fish.mouthBreath()}"
console.log "Fish #{fish.flapAround()}"
CommonJS is for all intents and purposes dead. Harmony’s module system will be the way to go in the future.
Express is a MVC framework for Node that is heavily inspired by Sinatra. I blogged about it recently.
express = require "express"
app = express.createServer()
app.get "/", (req, res) ->
res.send "Hello, world"
app.listen 8003
Exercises:
- Create an expressjs server (see above)
- Serve two different pages based on GET parameter “page”
- Create a redirect from /old to /new
- Set a cookie on the client
express.coffee
express = require "express"
app = express.createServer()
app.use express.logger
format: ":method :url"
app.use express.cookieParser()
app.get "/", (req, res) ->
if req.query.page is "1"
res.cookie "foo", 1
return res.send "First page, cookie is #{req.cookies.foo}"
if req.query.page is "2"
res.cookie "foo", "2"
return res.send "Second page"
res.clearCookie "foo"
res.send "Index"
app.get "/old", (req, res) ->
res.redirect "/new"
app.get "/new", (req, res) ->
res.send "This is the new page"
app.listen 8003
There is now a Gist for the actual code: https://gist.github.com/978411
Node.js started from having an event loop. When Ryan was writing it, he started with Ruby. But the problem was that many Ruby libraries weren’t event-driven. JavaScript was a fresh start.
The other option would be resource pre-allocation. That ensures fast serving for each user, but when you run out of resources performance drops dramatically.
With Node.js, you as a programmer only get one thread. OS-level events happen in their own threads. Blocking the main event loop is a bad idea.
There is a webworkers implementation, though: https://github.com/cramforce/node-wor... Most people however just fork
Node.js is all about I/O. Traditional programming is serial I/O: this, then that. Node follows parallel I/O: this and that at the same time.
- Unordered parallel: do this bunch of things, doesn’t matter which one happens first
- Ordered parallel: grouping parallel requests to handle necessary order: do this first, then that and that
Ordered serial I/O is bad for performance. Unordered parallel I/O is best performance, but makes it impossible to handle dependencies.
The “traditional” way of doing serial or ordered parallel I/O ends us to JavaScript, the bad parts. Nested callbacks: when we get a request, run this query, when query completes, send output.
There are patterns to help with that. Typically you allow a group of calls to be sent together with one callback. Then you just loop through the calls and run them with your own callback that counts responses. When all have responded, run the original callback.
…or then you run a library, like async
Exercises
- Use async.parallel to fetch 5 web pages and output the results to a file
- Use async.auto to get 2 files and then post them to your HTTP server
Answer to first, and to second
Async is giving me some ideas on how to do flow-based programming with Node.
Talking about Express. Regex-based routing is quite powerful
app.get('(/:id(\\d+)', callback); // matches only IDs that are numeric
- If . before
:var?
, then . is also optional - If
?
is not after a var, then only previous character is affected /
at end of URL is optional,/app/e?
means both last/
ande
are optional*
is wildcard- Regexps can be used in any place in route string
Any route callback can pass control to the next matching route by calling request.next()
. Next is a function in the router closure, so it will just step forward in the array of matched routes.
- Create a simple check for correct product IDs. If it fails, show a custom error page
- Use
app.all()
to check user permission before showing (mock) edit controls on a page
Theoretically any route with Express is also middleware. Creating your own is easy:
middleware = (req, res, next) ->
req.foo = "bar"
next()
app.use middleware
app.get "/", (req, res) ->
res.send req.foo
Middlewares are run before routes.
You can provide multiple middlewares to a single route:
a = (req, res, next) ->
req.foo = "bar"
next()
b = (req, res, next) ->
req.bar = "baz"
next()
app.get "/", [a, b], (req, res) ->
res.send "#{req.foo} #{req.bar}"
Middleware exercises:
- Create a middleware to detect a browser and attach a boolean to the request
- Create express app that servers links to images using staticProvider
- Modify profiler to profile your app and write profile data to a log file
- Create a middleware factory that sets the HTTP expires header based on roles
View exercises:
- Create Express server that uses some template engine (jade, ejs, whatever) to render an index page
- Create a static folder
- Create a route for
/blog/id
that only accepts digits - Create a “fake database” (array) of blogs, and use middleware to validate that ID is valid
- Create a view for the blog post
- Use a view partial to a preview blog posts on an index page
The blog example in coffeescript
Socket.io exercises… client and server
TCP sockets are also very easy with Node. My chat example now supports both web clients and regular telnet connections talking with each other.
Real-world Node.js deployment tips:
- Use cluster to scale up your deployment
- Ensure you catch errors (in worst case you can always
process.on 'uncaughtException'
) - NPM is a great way to package and install your application
For clustering, see the cluster manager and worker examples I made.
Complete list of exercise CoffeeScript solutions
JavaScript - the good parts :-P
Cool, my exercises made it to ReadWriteWeb: http://www.readwriteweb.com/hack/2011/05/coffeescript-nodejs-exercises.php