For the final post in this series, it’s time to connect to our API from our iOS app. You should have your mongo and node servers from the last tutorial running, and in your iOS app, let’s add the following method to create a guestbook entry:

- (void)createGuestbookEntry
{
    NSURL *url = [NSURL URLWithString: @"http://localhost:8765/guestbook/hello-from-iOS"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
    [request setHTTPMethod: @"POST"];
    [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSError *jsonError = nil;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &jsonError];
        NSLog( @"Response: %@", json );
    }];
}

We create an NSURL pointing to our API, and create an NSMutableURLRequest with that URL. We need this mutable version of NSURLRequest to do things like set it’s HTTP Method, and later on to add a body. In this case we’re sending an HTTP POST.

In the next line, we send an asynchronous request to the API, placing it’s callback block on the main queue. You typically put your callback block on the main thread so that you can update the UI accordingly, but if you’re doing any heavy-lifting in this block, then you’d specify a different queue here.

In that callback, we deserialize the JSON response to an NSDictionary and log it to the console. It’s output should look similar to this:

{
id = 5176f059d8dab83d0c000002;
}

Again, your ID will differ, so use your own ID for the remainder of these methods. Let’s see what the Read, Update and Delete methods look like:


- (void)readGuestbookEntry
{
    NSURL *url = [NSURL URLWithString: @"http://localhost:8765/guestbook/5176f059d8dab83d0c000002"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
    [request setHTTPMethod: @"GET"];
    [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSError *jsonError = nil;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &jsonError];
        NSLog( @"Response: %@", json );
    }];
}

- (void)updateGuestbookEntry
{
    NSURL *url = [NSURL URLWithString: @"http://localhost:8765/guestbook/5176f059d8dab83d0c000002"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
    [request setHTTPMethod: @"PUT"];

    NSString *bodyDataString = @"name=hello-again-from-iOS";
    NSData *bodyData = [bodyDataString dataUsingEncoding: NSUTF8StringEncoding];
    [request setHTTPBody: bodyData];
    [request addValue: @"application/x-www-form-urlencoded" forHTTPHeaderField: @"Content-Type"];

    [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSError *jsonError = nil;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &jsonError];
        NSLog( @"Response: %@", json );
    }];
}

- (void)deleteGuestbookEntry
{
    NSURL *url = [NSURL URLWithString: @"http://localhost:8765/guestbook/5176f059d8dab83d0c000002"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
    [request setHTTPMethod: @"DELETE"];
    [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSError *jsonError = nil;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &jsonError];
        NSLog( @"Response: %@", json );
    }];
}

Our ‘readGuestbookEntry’ method is pretty straight-forward. In ‘updateGuestbookEntry’, our HTTP PUT sends body data along, and we specify a special HTTP header here to tell our API how the data is arriving. This happened behind the scenes in curl but is a necessary header when sending body data.

You’ll note that ‘deleteGuestbookEntry’ prints a null response, and if you refer back to our API code, you’ll remember that we did not send back any data.

If any of these API calls returned an error, or a network error happened (we didn’t start our server), we could handle those errors by doing something like the following:

if ( error != nil )
{
    NSLog( @"Error communicating with API: %@:%@", error, [error userInfo] );
    return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ( httpResponse.statusCode != 200 )
{
    NSString *errorString = [NSHTTPURLResponse localizedStringForStatusCode: [httpResponse statusCode]];
    NSLog( @"Error returned from API: %d: %@", [httpResponse statusCode], errorString );
    return;
}

You can now setup and communicate with a node API from iOS. Some next steps:

  • Protect your API with an authentication scheme such as HMAC
  • Deploy your API to EC2

Both of which are material for a future post. If you found this post helpful, let me know. Congrats on making it through!

From reading the past tutorials you should be starting to get a hang of API development, so we’re going to take a big leap forward and persist some data to MongoDB. We’ll create a very simple guestbook API, which records guestbook entries, reads them, updates them and deletes them. Install the latest version of MongoDB and let’s get started.

Let’s create a separate folder for this part of the tutorial and call it “part-5”. We’ll want to start MongoDB and keep it running, but beforehand we have to create a configuration file. In your part-5 folder, create a file called mongodb.conf and add the following

dbpath = db/
bind_ip = 127.0.0.1
noauth = true # use 'true' for options that don't take an argument

The first line instructs MongoDB to place it’s files in the “db” folder, but it won’t create that folder for us, so go ahead and do that now. The second line mentions which IP address to listen on. Throughout this tutorial we’ve seen 127.0.0.1. If you haven’t guessed by now, 127.0.0.1 is the IP address which DNS maps to from “localhost”, and it’s a loopback address; it always points to your own computer / device. Since we’re only listening on the local IP address, we’ll set noauth = true to avoid having to authenticate.

With our db folder created and the mongodb.conf file ready, let’s start mongodb with the following:

mongod --config mongodb.conf

You should now see a bunch of output on the screen, and mongo will be waiting for connections on port 27017. Leave that running for the rest of this tutorial.

Next up, we’re going to need to talk to our DB, and we’ll do that with a driver called Mongoose. It’s a third-party node package, so we’ll install it with npm:

npm install mongoose

If you’re in a different folder than you used for the last tutorial, remember to install restify, too:

npm install restify

In the last tutorial we separated our request handlers out from our main index.js file, to keep things tidy. Our index.js file for the Guestbook API will look similar. Copy and paste the following into a file saved as index.js:

var restify = require("restify");
var mongoose = require("mongoose");
var guestbookHandler = require("./guestbookHandler");

mongoose.connect("mongodb://localhost/guestbook");

var server = restify.createServer();
server.use(restify.bodyParser());

server.post( "/guestbook/:name", guestbookHandler.handleCreate );
server.get( "/guestbook/:id", guestbookHandler.handleRead );
server.put( "/guestbook/:id/", guestbookHandler.handleUpdate );
server.del( "/guestbook/:id", guestbookHandler.handleDelete );

server.listen( 8765 );
console.log( "server is running" );

We start out by including our restify and mongoose packages. “guestbookHandler” is a file we’ll get to a bit later on. You can see here we connect to mongodb using a special URL. Note that the last part of the URL, “/guestbook”, is arbitrary: we can call it anything we like, just remember it for accessing mongo in the shell, which we’ll do later on.

The next line creates our restify server, and then we have something new:

server.use(restify.bodyParser());

Here we’re instructing restify to parse data sent up with our request (in the request body), so that we can access it easily through the req.params object. We haven’t sent request bodies yet with curl, but you’ll see how this works later on.

The next 4 lines should look familiar: we’re defining our routes, and the functions that will handle them. We have a handler for HTTP POST, GET, PUT and DELETE, which map to Create, Read, Update and Delete (CRUD). We name some parameters in the URLs for easy access with req.params.

Finally, we tell our server to start on port 8765 and then output a log message to the console.

We’ve kept our index.js file clean by separating the request handlers into their own file, so let’s see what that file looks like. Copy and paste the following code into a new file named guestbookHandler.js:

var restify = require("restify");
var guestbook = require("./guestbook" );

function handleCreate(req, res, next)
{
    var name = req.params.name;
    return guestbook.createEntry( name, createCallback );

    function createCallback(err, id)
    {
    if ( err )
        return next( new restify.InternalError( err ) );

    res.send( { id : id } );
    return next();
    }
}

function handleRead(req, res, next)
{
    var id = req.params.id;
    return guestbook.readEntry( id, readCallback );

    function readCallback(err, guestbookDoc)
    {
        if ( err )
            return next( new restify.InternalError( err ) );

        res.send( guestbookDoc );
        return next();
    }
}

function handleUpdate(req, res, next)
{
    var id = req.params.id;
    var name = req.params.name;

    return guestbook.updateEntry(id, name, updateCallback );

    function updateCallback(err, guestbookDoc)
    {
        if ( err )
            return next( new restify.InternalError( err ) );

        res.send( guestbookDoc );
        return next();
    }
}

function handleDelete(req, res, next)
{
    var id = req.params.id;
    return guestbook.deleteEntry( id, deleteCallback );

    function deleteCallback(err)
    {
        if ( err )
            return next( new restify.InternalError( err ) );

        res.end();
        return next();
    }
}

exports.handleCreate = handleCreate;
exports.handleRead = handleRead;
exports.handleUpdate = handleUpdate;
exports.handleDelete = handleDelete;

There’s a lot here, so let’s walk through it.

var restify = require("restify");
var guestbook = require("./guestbook" );

The second line is including a local file called “guestbook.js”, which we’ll get to in a bit.

function handleCreate(req, res, next)
{
    var name = req.params.name;
    return guestbook.createEntry( name, createCallback );

    function createCallback(err, id)
    {
    if ( err )
        return next( new restify.InternalError( err ) );

    res.send( { id : id } );
    return next();
    }
}

In handleCreate, we get the parameter called ‘name’ from the URL route we specified in index.js (remember the ‘/:name’ part). This should seem familiar: we’ll be expecting a URL like /guestbook/Richard, and req.params.name will contain ‘Richard’. Next we have a bit of asynchronous code, so read it carefully. We’re returning guestbook.createEntry on the next line, and passing along the name of a callback, in this case our createCallback function. So even though we’re returning here, our ‘createCallback’ will be invoked later on when guestbook.createEntry is finished. While this ‘return’ statement is not required, it’s best practice to put a return on your last line, both for readability and to avoid errors. Note that your ‘last line’ often comes before your callbacks.

The createCallback function is fairly straight forward. It accepts two parameters: an error, and an ID for the newly created document. If an error is returned, we use the shorthand of returning next() with that error specified. The next() function will propagate that error back to the client, so for bonus points we could call next() with the appropriate HTTP status code for that error.

If there was no error, we want to send the ID back to the client. We pass the ID to res.send() in JSON format, then return next() which will end the response.

The remaining functions follow the same form: grab any input we expect from the request and pass it along to our ‘guestbook’ object, with a callback defined below. We end this file with some exports to make these functions visible to index.js.

Last but not least, we get to our CRUD file. This is the file that handles all of our data persistence. We’ve kept our guestbook MongoDB code in one file so that changing the way we talk to our database is simple in the future. Copy and paste the following code into a new file called guestbook.js:

var mongoose = require("mongoose");

var guestbookSchema = new mongoose.Schema(
{
    name : String,
    dateVisited : { type: Date, default: Date.now, index: true }
});
var Guestbook = mongoose.model( "guestbookEntry", guestbookSchema );

function createEntry(name, callback)
{
    var guestbookEntry = new Guestbook( { name: name } );
    return guestbookEntry.save( guestbookSaveCallback );

    function guestbookSaveCallback(err, guestbookDoc)
    {
        return callback( err, guestbookDoc._id );
    }
}

function readEntry(id, callback)
{
    return Guestbook.findOne( {_id : id }, guestbookFindCallback );

    function guestbookFindCallback(err, guestbookDoc)
    {
        return callback( err, guestbookDoc );
    }
}

function updateEntry(id, name, callback)
{
    return readEntry( id, readEntryCallback );

    function readEntryCallback(err, guestbookDoc)
    {
        if ( err )
        {
            return callback( err, null );
        }

        guestbookDoc.name = name;
        return guestbookDoc.save( guestbookDocSaveCallback );
    }

    function guestbookDocSaveCallback(err, updatedGuestbookDoc)
    {
        return callback( err, updatedGuestbookDoc );
    }
}

function deleteEntry(id, callback)
{
    return Guestbook.remove( { _id : id }, guestbookRemoveCallback );

    function guestbookRemoveCallback( err )
    {
        return callback( err );
    }
}

exports.createEntry = createEntry;
exports.readEntry = readEntry;
exports.updateEntry = updateEntry;
exports.deleteEntry = deleteEntry;

Again, there’s a lot here, so let’s talk through it:

var mongoose = require("mongoose");

var guestbookSchema = new mongoose.Schema(
{
    name : String,
    dateVisited : { type: Date, default: Date.now, index: true }
});
var Guestbook = mongoose.model( "guestbookEntry", guestbookSchema );

We include the mongoose node package, and then define our Schema. In this case, our schema has a name, which is a string, and dateVisited, for which we specify a few extra properties: it’s of type Date, and has a default value of Date.now, so we don’t have to manually specify the Date when we create our documents. We also ask MongoDB to index this property, so that looking up documents by their dateVisited field stays fast in large collections. Note also that the documents have a built-in “_id” property, which is indexed and unique, and created automatically with each document.

In the next line, we create a Model with this schema. Our Guestbook model will store it’s data in a collection called “guestbookEntry”. If we wanted to store a second collection separately, say a private guestbook, with the same desired schema, we could add a second line here replacing “guestbookEntry” with, say, “privateGuestbookEntry”.

One more note about schemas: MongoDB uses dynamic schemas, meaning we can add and remove properties as we like. This is handy, because later on if we want to, say, add a ‘comment’ field to our schema, we can just tack it on and start using it. We can also decide to start putting, say, a JSON object containing “lastName” and “firstName” into our “name” field. This flexibility can also lead to problems: if we accidentally stuff a large JSON object into “name” for example, we may suddenly find our database performance dropping without understanding why. To prevent these kinds of mistakes, you can enforce a “strict” option when creating your Mongoose Schema.

Continuing on:

function createEntry(name, callback)
{
    var guestbookEntry = new Guestbook( { name: name } );
    guestbookEntry.save( guestbookSaveCallback );

    function guestbookSaveCallback(err, guestbookDoc)
    {
        return callback( err, guestbookDoc._id );
    }
}

In createEntry, we start by creating a Guestbook Document. The Guestbook constructor accepts a JSON object, and note that we’re only specifying ‘name’, because dateVisited has a default value. We then call guestbookEntry.save() with a callback. That callback has 2 parameters: err, and guestbookDoc. If an error occurs, it’s passed in “err”, and guestbookDoc will likely be null. Otherwise, guestbookDoc contains our newly created Mongoose document, and so we invoke our callback with it’s _id property mentioned earlier.

function readEntry(id, callback)
{
    return Guestbook.findOne( {_id : id }, guestbookFindCallback );

    function guestbookFindCallback(err, guestbookDoc)
    {
        return callback( err, guestbookDoc );
    }
}

This code should be readable by now. Our Guestbook model has a findOne method, and we pass in a query specifying that the _id property should match our ‘id’ variable.

If you’ve followed along this far, you should be able to read the remainder of the file: our Update and Delete methods.

After you’ve saved these 3 files, start our server with the usual command:

node index.js

Let’s test our server with some curl commands. First of all, let’s create a guestbook entry:

curl -X POST http://localhost:8765/guestbook/richard

We tell curl to issue an HTTP POST to our local server. If you’ve followed along carefully, you should get back the JSON we returned containing the ID:

{"id":"5176db93d8dab83d0c000001"}

Your ID will likely differ, so use your own value in the upcoming curl statements. OK, so let’s take a look at the full document by issuing a GET

curl http://localhost:8765/guestbook/5176db93d8dab83d0c000001

Note that we could have explicitly specified that this is a GET by issuing the -X GET command, but GET is the default HTTP method in curl. This should return the full JSON document:

{"name":"richard","_id":"5176db93d8dab83d0c000001","__v":0,"dateVisited":"2013-04-23T19:05:55.212Z"}

We see the name and dateVisited properties, along with the built-in _id property and the system field “__v” which is used for versioning.

We’ve created and read the document, let’s try updating it:

curl -X PUT http://localhost:8765/guestbook/5176db93d8dab83d0c000001 -d "name=penner"

You’ll notice something new here. We’ve added a “data” parameter to pass in “name”. You’ll remember that back in our index.js file we had restify “use” the bodyParser, so this “name” data will show up in our req.params object. This is a convenience; we could manually pull it out of the body if restify was not parsing this for us.

If that went OK, you’ll have your updated document returned:

{“name”:”penner”,”_id”:”5176db93d8dab83d0c000001″,”__v”:0,”dateVisited”:”2013-04-23T19:05:55.212Z”}

Before we get to the delete, which will remove our document, I want to touch on one more part of MongoDB: the mongo shell. If you’re curious where all of this data is going, open a new Terminal tab and type in:

mongo guestbook

That will connect you to our guestbook instance. From there, type in db.getCollectionNames() and start poking around. You’ll notice, for example, that while we instructed Mongoose to store our documents in “guestbookEntry”, it’s magically pluralized the collection for us:

> db.getCollectionNames()
[ "guestbookentries", "system.indexes" ]

To see our document, type:

> db.guestbookentries.find()
{ "__v" : 0, "_id" : ObjectId("5176db93d8dab83d0c000001"), "dateVisited" : ISODate("2013-04-23T19:05:55.212Z"), "name" : "penner" }

Hit Ctrl-C to exit the mongo shell. Finally, let’s test our delete handler with the following curl command:
curl -X DELETE http://localhost:8765/guestbook/5176db93d8dab83d0c000001

If you return to the mongo shell now, your collection will be empty.

If you’ve made it this far, you’re on the home stretch. You can now write APIs with cleanly-separated handlers, and you can persist your data to MongoDB. Best of all, you can fully test your API with curl.

To wrap things in part 6, we take a look at how to talk to this API in iOS.

You’ve come a long way. You’ve mimicked a web browser and a web server, and even wrote a simple web server in Node.js.

Our server in it’s current state is far too simple to be of any use. Any requests coming from any URL respond in the same way. In this part of the series we’re going to look at how to structure our server in a more manageable, realistic way.

To do this, we need to learn a few more things about Node.js. First, you’ll recall we made use of a node package called HTTP. HTTP is a built-in package, but there are many third party packages available to us from the node community. We’re going to use Restify, which is a nice library meant for assisting the development of REST APIs. It helps us easily map requests for specific URLs to their appropriate handlers. For full documentation, visit Restify on github, or read through the API docs.

To install the Restify package, open a Terminal window and in the folder where you want your Node application to live, run the following command:

npm install restify

This installs the Restify node package along with any dependencies. Let’s see what our node server looks like with Restify helping us out:

var restify = require("restify");

var server = restify.createServer();

server.get( "/hello/:firstname/:lastname", handleHello );
server.get( "/goodbye/:firstname/:lastname", handleGoodbye );

server.listen( 8765 );

function handleHello(req, res, next)
{
    res.end( "Hello "+req.params.firstname+" "+req.params.lastname );
    return next();
}

function handleGoodbye(req, res, next)
{
    res.end(" Goodbye "+req.params.firstname+" "+req.params.lastname );
    return next();
}

You can run this the same way we did last time: save it as index.js and issue

node index.js

from the folder you’ve saved it to.

In our code we’re now creating a restify server rather than the built-in HTTP server. Note these lines:

server.get( "/hello/:firstname/:lastname", handleHello );
server.get( "/goodbye/:firstname/:lastname", handleGoodbye );

We’re telling our server ‘when you see an HTTP GET to /hello/something/another-thing, send that to our handleHello function.’ We name those parameters ‘firstname’ and ‘lastname’, and in our handleHello and handleGoodbye functions, we access them through the req.params object.

But we’ve hardly improved on the server from our last instalment. Yes, we can now map URLs to functions, but with any modest-sized API, our index.js file is going to become large and hideous. So let’s break out the handler functions to their own files. ‘hello.js’ for example, looks like this:

function handleHello(req, res, next)
{
    res.end( "Hello "+req.params.firstname+" "+req.params.lastname );
    return next();
}

exports.handleHello = handleHello;

The only thing new here is the exports line. This line instructs node to make our handleHello function accessible to other files. ‘goodbye.js’ looks very much the same, and index.js now looks like this:

var restify = require("restify");
var hello = require("./hello");
var goodbye = require("./goodbye");

var server = restify.createServer();

server.get( "/hello/:firstname/:lastname", hello.handleHello );
server.get( "/goodbye/:firstname/:lastname", goodbye.handleGoodbye );

server.listen( 8765 );

Note the use of “./hello” and “./goodbye”; we’re including local files, so we give it the “./” current folder prefix.

We can now write a reasonably large API with our code organized into files for each of the requests we handle.

With that covered, we have one more server topic to address before writing our client app. Our API won’t be of much use to a client just yet unless it can actually persist data. In part 5 of the tutorial, we make our API read and write to a local MongoDB server.

So far in this tutorial series, we’ve imitated a basic web client, and then mimicked a barebones web server. This was all done from command line tools, but it’s time to write some code. We’re going to write a web server using Node.js. If you haven’t heard of it before, Node.js is Javascript, run server-side. Visit nodejs.org to download the installer.

With node installed, open your favourite text editor and paste in the following:

var http = require("http");
var server = http.createServer( handleRequest );
server.listen( 8765, "127.0.0.1" );
console.log( "server started" );
function handleRequest(req, res)
{
    res.writeHead( 200, { "Content-Type":"text/plain"} )
    res.end( "Richard Penner is really knocking it out of the park with this tutorial series.\n" );
}

Save that file as ‘index.js’, and run it from terminal by issuing:

node index.js

You should now be able to hit your server in your web browser at http://localhost:8765. While I won’t make this a Node.js tutorial, let’s walk through this line by line.

var http = require("http");

Here we’re telling node to include the HTTP library, or “package” in node parlance. The HTTP library let’s us easily create, well, an HTTP server, which we do in the next line:

var server = http.createServer( handleRequest );

Note that we pass in a function as an argument. Node.js is very fast, single-threaded, and non-blocking. Non-blocking means we’re going to be passing in callback functions anytime we’re doing something potentially slow. Handling a request might be slow: if our first user uploads a large file, we don’t want the second user waiting on that file upload to complete before we serve them. So we pass in a callback which will be invoked whenever a request comes in to be served. The next line is easy:

server.listen( 8765, "127.0.0.1" );

We’re going to listen on port 8765 of our local IP address. Continuing on, we do some logging to the console, and then we get to the good stuff:

function handleRequest(req, res)
{
    res.writeHead( 200, { "Content-Type":"text/plain"} );
    res.end( "Richard Penner is really knocking it out of the park with this tutorial series.\n" );
}

This is our callback function, which is invoked by our server object when we receive a request. It’s passed two things: the incoming request (req), and the outgoing response (res). Remember those HTTP headers we saw the browser sending along, identifying the browser and such? You can peel those out of the req.headers array.

We start by writing the HTTP status code: 200, which you’ll recall means “OK”, as in, this request worked. If req.url was something invalid like “/your-non-existent-page/”, we may write a 404 status code instead.

We also specify a “Content-Type” header of “text/plain”, letting the client know what format we’re returning our response in. Finally, we call the response object’s end() function which lets us pass in a string of data to send back to the client, in this case, another generous complement – you’re really too much.

So we now have a web server running locally. But it’s a very simple server. In part 4, we continue our look at Node.js by beefing up our server to reflect a little closer what a real world API might look like.

In the first part of this series, we learned a bit about networking and HTTP communication. We pretended to be a web browser, and spoke to some web servers from the command line.

We’re now going to view the conversation from the other side, and pretend to be a web server. To do this, we’re going to use a linux command called netcat. For the uninitiated, netcat is the swiss army knife of networking. We want to setup a server, which means we’ll open up a port and listen for incoming connections. This is done with a simple netcat command:

nc -l 8765

We’ve told netcat to start listening on port 8765. As you know, the default web port is 80: typing http://google.ca is the same thing as typing http://google.ca:80. We’re using a non-standard port, so open a new tab in your browser and type in

http://localhost:8765

After you press enter, take a look at your console output. I used the latest version of Safari, but any browser should work:

GET / HTTP/1.1
Host: localhost:8765
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.28.10 (KHTML, like Gecko) Version/6.0.3 Safari/536.28.10
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive

This should look familiar by now. The browser here has sent the same command we saw using curl in part 1: GET / HTTP/1.1, or in plain-english: use the GET HTTP method to retrieve the root document, and speak using HTTP 1.1. What follows are some HTTP headers sent by the browser to let our server know what it’s capable of. For example, we can see the language is set to ‘en-us’, and that our browser allows large web pages to be compressed with gzip.

You’ll notice that your browser is still not finished loading the page, and your console command is still running. Well, like I said, we’re pretending to be a web server, so let’s serve some content. Go ahead and type in anything you like, making sure to hit enter twice afterward:

this is the best tutorial i've ever read in my life

Well that’s quite generous of you, but that’s not valid HTML. No matter, when you’re finished paying me your respects, hit Ctrl-C to kill netcat and close the connection. Now if you look at your browser, you should see your message.

So far in the tutorial, we’ve pretended to be both a web client and a web server. What does this have to do with mobile, and why haven’t I mentioned anything about APIs yet? Well, most modern APIs today communicate using these same HTTP commands used by your web browser talking to web servers. And as a mobile developer, sooner or later you’ll need to talk to these APIs.

In part 3, we take what we’ve learned so far and implement a real HTTP API server using Node.js.

This post is the first in a series of articles intended to teach some networking and HTTP basics to mobile developers. While any developer looking to learn more about REST APIs or HTTP can benefit from reading this, the focus will be on consuming HTTP APIs from an iOS app.

To begin with, let’s go over some brief terms you are hopefully quite familiar with:

  • IP Address: A numerical address identifying you on a network. If you are reading this on your phone over a cellular network for example, your IP address is public and uniquely identifies your phone on the internet. Reading this from home, you may have a cable modem with a public IP address, and a router which maps private IP addresses from your devices through the public IP address of your cable modem. IP addresses are of the form aaa.bbb.ccc.ddd where each set is an 8 bit number (0-255). Out of a possible 2^32 IP addresses available, some IP addresses are designated as private, for use on an internal network (behind your cable modem). If 2^32 (4,294,967,296) doesn’t sound like a lot of IP addresses, you’re right; we’re running out of them. Thankfully, a more modern IP address format called IPv6, has gained adoption and some of your devices may already be using them.
  • Port: If you imagine an IP address as a street address, then every house on that street can receive letters in a number of different mailboxes. 65,536 different mailboxes, to be exact. Every web page you download, every email you receive, all network connections go to and from one of these ports. Web browsers know to communicate on port 80. If you visit an SSL-encrypted web page, your browser knows to hit 443.
  • HTTP methods: When we pull down a web page, our browser is issuing an HTTP GET request along with a specific URL. This is telling the server that we want to receive the contents of whatever is at that location. For example, if you’re browsing the New York Times, your browser likely issued a GET request for “/”, where “/” is the root document. Click a link and your browser issues a GET for /pages/travel/index.html, for example. The verbs you’ll concern yourself with when consuming an HTTP API are GET, POST, DEL, and PUT. While the implementation of each of these verbs is up to the API developer, these methods typically map to CRUD (create, read, update delete) of server resources. We’ll dig more into that later on.
  • HTTP status code: HTTP status codes are a way for the server to tell our browser whether things are working as we intended. For example, if we request the root document “/” and receive an HTTP status code of 200 (which represents “OK”), our browser knows everything worked fine. A status code of 301 means the web page has moved permanently, and you should update your link or bookmark.
  • REST: Representational State Transfer. In short, this is how the web is organized. The resource you want to consume, say a web page, lives at a certain address, like http://example.com/webpage.html. You use the HTTP methods listed above to interact with those resources.

OK, so we know about IP addresses, ports, and a tiny bit about HTTP. Now what? Let’s pretend to be a web browser. This will give us some insight into how browsers work, and more importantly, what HTTP communication looks like. We’re going to use cURL, which is an HTTP command-line utility usually bundled with linux and Mac OS/X and available for most other platforms. Open a Terminal window and type the following:

curl --verbose http://google.ca

You may want to substitute google.com if you’re reading this from the U.S. Let’s walk through the output:

* About to connect() to google.ca port 80 (#0)
* Trying 173.194.43.120...
* connected
* Connected to google.ca (173.194.43.120) port 80 (#0)

In the first line we can see that curl is attempting to hit google.ca on port 80, which we know is the web port. The second line shows the IP address that google.ca maps to. Alright, one additional note about curl: output denoted with “>” means we’re sending it, and “<” means we’ve received it. Continuing through the output:

> GET / HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: google.ca
> Accept: */*

Here we can see that curl issued the HTTP GET method, and is requesting the root document, “/”. We’re communicating with HTTP 1.1, which is better than 1.0 in a few ways, one being that we can keep our network connection open when requesting several items. Next, curl sends some HTTP headers. Firstly, curl is telling google what our User-Agent string is. This is not required but is a nice way of telling the server a bit about us, in case the server wants to customize it’s output (send us a mobile-friendly page, etc.). Following that is the Host header, which as part of HTTP 1.1 specifies which host (server) we want the document from; keep in mind that a web page may contain links to images and scripts from many different hosts. Finally, the Accept header informs the server what kind of documents we are willing to receive. Here we tell the server that we’ll accept anything (*/*). Next:

< HTTP/1.1 301 Moved Permanently
< Location: http://www.google.ca/

For the scope of this tutorial I’ve trimmed out some of the response, but let’s note the 301 status code. You’ll remember that 301 means this document was moved permanently. In the next line, we see it’s new location: http://www.google.ca. Alright, so when our browser hits http://google.ca, the google server tells us to go grab http://www.google.ca instead. The response ends with some HTML for the browser to display:

<HTML><HEAD><meta http-equiv=”content-type” content=”text/html;charset=utf-8″>
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF=”http://www.google.ca/”>here</A&gt;.
</BODY></HTML>

* Connection #0 to host google.ca left intact
* Closing connection #0

Note that you won’t see this HTML in most browsers; your browser follows that 301’s location automatically. Alright, so google told us to add the www, let’s try the new location:

curl --verbose http://www.google.ca

The output begins with the same GET request and HTTP headers passed along, but this time, success:

< HTTP/1.1 200 OK
< Date: Wed, 17 Apr 2013 18:43:38 GMT
< Set-Cookie: PREF=ID=14d0f143b176675f:FF=0:TM=1366224218:LM=1366224218:S=FctofHxDhvyJlJBS; expires=Fri, 17-Apr-2015 18:43:38 GMT; path=/; domain=.google.ca
< Set-Cookie: NID=67=jHYpBlAGKIEMsPyfnCJ3Z-0McLdcjVZPrtHOPnKPKCt_Zqcqq5Cl1Q30R6nNv5QkLcS2qzeVmJNxkRVu3u1_Ob1fmPGK3fMdLuCNC3tWsQBwfdRnRtVxFTb1KFOr2L0G; expires=Thu, 17-Oct-2013 18:43:38 GMT; path=/; domain=.google.ca; HttpOnly

And what follows is the HTML source of Google’s home page.

We now know the basics of HTTP communication, and how to talk to a web server. In the next part of this tutorial, we pretend to be a web server and view the HTTP conversation from the other side.