Getting Started with Web Services Using Node and Express (for Cocoa developers)
(See Getting Started if you haven’t already.)
Let’s create an API. I could do something with databases — because that’s probably what you really need — but to keep things simple I’ll leave databases out of it. (I might get to them in a future post.)
Don’t Use Node Without It
You need Express. There are a ton of packages for Node — it’s a very active community. Express is a web app framework that takes care of a bunch of common things you’d otherwise have to write yourself. (If Node is Foundation, Express is UIKit or AppKit. Roughly.)
Node’s package manager npm makes installing packages easy. I believe it’s installed with Node, so you should already have it.
New App
Create a folder called expressdemo
.
In that folder create a file called package.json
. It’s like a Cocoa app’s Info.plist — it contains some basic information about your app, including its name, description, version, and a list of Node packages it depends on.
The file should look like this:
{
"name": "expressdemo",
"description": "Checking out Express",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.x"
}
}
If you’re not already in Terminal, open it up and navigate to that folder. Then type:
npm install
This will read the package.json
file and install Express and any additional dependencies in a node_modules
folder inside the expressdemo
folder. (This keeps your app self-contained. You don’t have to wonder if the system has the right modules installed; you don’t have to wonder if the system has the right versions of various modules installed.)
Design the API
Our API does two simple things:
-
Returns the current date in a JSON dictionary.
-
Returns the base64-encoded version of a string we pass to it.
We want URLs like this:
/now
- returns date
/base64?s=someString
- returns base64 of someString
Create the Server
Inside the expressdemo
folder create server.js
with these lines at the top:
var express = require('express');
var http = require('http');
var app = express();
app.set('port', process.env.PORT || 1337);
Our app requires the express and http modules, and it creates an app by calling express()
. It sets the port for listening from an environment variable — but, if that hasn’t been set, it uses 1337.
(Why an environment variable? Because the port shouldn’t be hard-coded. However, it’s useful to have a fallback for when you’re running locally, as we are now.)
Then add the code that starts the server:
http.createServer(app).listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
Add the API Code
We have two endpoints to respond to. Both use the GET method. Add the function for /now
.
app.get('/now', function(request, response) {
var d = new Date();
response.send(200, {date: d});
});
Note the response.send
parameters. The first parameter is the HTTP status code, and the second is the response body. Since the response body is a JavaScript dictionary, the response is returned as JSON. (That’s how easy returning JSON is.)
Then let’s add the /base64 function:
app.get('/base64', function(request, response) {
var stringToEncode = request.query.s;
var base64EncodedString = new Buffer(stringToEncode, 'utf8').toString('base64');
response.send(200, base64EncodedString);
});
This time the response body is a string, so it’s returned as itself rather than as JSON.
Note that we got the string to encode via the request.query
dictionary.
Save server.js
and run it from Terminal: node server.js
Testing
Open a new tab in Terminal and try a few things:
curl -I http://localhost:1337/
The result should be something like this:
HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Type: text/html
Date: Tue, 10 Dec 2013 05:07:26 GMT
Connection: keep-alive
(The -I switch shows just the response headers.)
curl -I http://localhost:1337/now
The /now endpoint returns the date in a JSON dictionary. The Content-Type header should specify JSON — even though our code didn’t specify that explicitly — and it does:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 40
Date: Tue, 10 Dec 2013 05:09:07 GMT
Connection: keep-alive
The /base64 endpoint does not return JSON, and the Content-Type header should specify text. And it does:
curl -I 'http://localhost:1337/base64?s=foo'
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 4
Date: Tue, 10 Dec 2013 05:10:43 GMT
Connection: keep-alive
The /now endpoint should actually return JSON with something that looks like a date:
curl http://localhost:1337/now
{
"date": "2013-12-10T05:13:21.772Z"
}
The base64 endpoint should return the input string as base64. We know that foo
encodes as Zm9v
.
curl 'http://localhost:1337/base64?s=foo'
Zm9v
It works!
Notes
That wasn’t a bunch of code, and you’ve got two endpoints, and one even returns results as JSON. So easy.
Your next step might be to write some unit tests. For that I recommend Mocha.