How To Write A Simple Node.js/MongoDB Web Service for an iOS App
Learn how to create a simple Node.js and MongoDB web service for an iOS app in this tutorial. By Michael Katz.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Write A Simple Node.js/MongoDB Web Service for an iOS App
50 mins
- A Case for Node+Mongo
- Getting Started
- Running a Node.js Script
- Node Packages
- NPM – Using External Node Modules
- Using Express
- A Short Diversion into HTTP Verbs
- Adding a Package to Your Node Instance
- Serving up Content With Express
- Advanced Routing
- Error Handling And Templated Web Views
- Introducing MongoDB
- Adding MongoDB to Your Project
- Creating a MongoDB Collection Driver
- Using Your Collection Driver
- Working With Data
- Updating and Deleting Data
- Where to Go From Here?
Using Your Collection Driver
To call your collectionDriver
, first add the following line to the dependencies
section of package.json:
"mongodb":"1.3.23"
Execute the following command in Terminal:
npm update
This downloads and installs the MongoDB package.
Execute the following command in Terminal:
edit views/data.jade
Now add the following code to data.jade, being mindful of the indentation levels:
body
h1= collection
#objects
table(border=1)
if objects.length > 0
- each val, key in objects[0]
th= key
- each obj in objects
tr.obj
- each val, key in obj
td.key= val
This template renders the contents of a collection in an HTML table to make them human-readable.
Add the following code to index.js, just beneath the line path = require('path'),
:
MongoClient = require('mongodb').MongoClient,
Server = require('mongodb').Server,
CollectionDriver = require('./collectionDriver').CollectionDriver;
Here you include the MongoClient
and Server
objects from the MongoDB module along with your newly created CollectionDriver
.
Add the following code to index.js, just after the last app.set
line:
var mongoHost = 'localHost'; //A
var mongoPort = 27017;
var collectionDriver;
var mongoClient = new MongoClient(new Server(mongoHost, mongoPort)); //B
mongoClient.open(function(err, mongoClient) { //C
if (!mongoClient) {
console.error("Error! Exiting... Must start MongoDB first");
process.exit(1); //D
}
var db = mongoClient.db("MyDatabase"); //E
collectionDriver = new CollectionDriver(db); //F
});
Line A above assumes the MongoDB instance is running locally on the default port of 27017. If you ever run a MongoDB server elsewhere you’ll have to modify these values, but leave them as-is for this tutorial.
Line B creates a new MongoClient and the call to open
in line C attempts to establish a connection. If your connection attempt fails, it is most likely because you haven’t yet started your MongoDB server. In the absence of a connection the app exits at line D.
If the client does connect, it opens the MyDatabase database at line E. A MongoDB instance can contain multiple databases, all which have unique namespaces and unique data. Finally, you create the CollectionDriver
object in line F and pass in a handle to the MongoDB client.
Replace the first two app.get
calls in index.js with the following code:
app.get('/:collection', function(req, res) { //A
var params = req.params; //B
collectionDriver.findAll(req.params.collection, function(error, objs) { //C
if (error) { res.send(400, error); } //D
else {
if (req.accepts('html')) { //E
res.render('data',{objects: objs, collection: req.params.collection}); //F
} else {
res.set('Content-Type','application/json'); //G
res.send(200, objs); //H
}
}
});
});
app.get('/:collection/:entity', function(req, res) { //I
var params = req.params;
var entity = params.entity;
var collection = params.collection;
if (entity) {
collectionDriver.get(collection, entity, function(error, objs) { //J
if (error) { res.send(400, error); }
else { res.send(200, objs); } //K
});
} else {
res.send(400, {error: 'bad url', url: req.url});
}
});
This creates two new routes: /:collection
and /:collection/:entity
. These call the collectionDriver.findAll
and collectionDriver.get
methods respectively and return either the JSON object or objects, an HTML document, or an error depending on the result.
When you define the /collection
route in Express it will match “collection” exactly. However, if you define the route as /:collection
as in line A then it will match any first-level path store the requested name in the req.params
. collection in line B. In this case, you define the endpoint to match any URL to a MongoDB collection using findAll
of CollectionDriver
in line C.
If the fetch is successful, then the code checks if the request specifies that it accepts an HTML result in the header at line E. If so, line F stores the rendered HTML from the data.jade
template in response
. This simply presents the contents of the collection in an HTML table.
By default, web browsers specify that they accept HTML in their requests. When other types of clients request this endpoint such as iOS apps using NSURLSession
, this method instead returns a machine-parsable JSON document at line G. res.send()
returns a success code along with the JSON document generated by the collection driver at line H.
In the case where a two-level URL path is specified, line I treats this as the collection name and entity _id
. You then request the specific entity using the get()
collectionDriver
‘s method in line J. If that entity is found, you return it as a JSON document at line K.
Save your work, restart your Node instance, check that your mongod
daemon is still running and point your browser at http://localhost:3000/items
; you’ll see the following page:
Hey that’s a whole lot of nothing? What’s going on?
Oh, wait — that’s because you haven’t added any data yet. Time to fix that!
Working With Data
Reading objects from an empty database isn’t very interesting. To test out this functionality, you need a way to add entities into the database.
Add the following method to CollectionDriver.js and add the following new prototype method just before the exports.CollectionDriver
line:
//save new object
CollectionDriver.prototype.save = function(collectionName, obj, callback) {
this.getCollection(collectionName, function(error, the_collection) { //A
if( error ) callback(error)
else {
obj.created_at = new Date(); //B
the_collection.insert(obj, function() { //C
callback(null, obj);
});
}
});
};
Like findAll
and get
, save
first retrieves the collection object at line A. The callback then takes the supplied entity and adds a field to record the date it was created at line B. Finally, you insert the modified object into the collection at line C. insert
automatically adds _id
to the object as well.
Add the following code to index.js just after the string of get
methods you added a little while back:
app.post('/:collection', function(req, res) { //A
var object = req.body;
var collection = req.params.collection;
collectionDriver.save(collection, object, function(err,docs) {
if (err) { res.send(400, err); }
else { res.send(201, docs); } //B
});
});
This creates a new route for the POST verb at line A which inserts the body as an object into the specified collection by calling save()
that you just added to your driver. Line B returns the success code of HTTP 201 when the resource is created.
There’s just one final piece. Add the following line to index.js just after the app.set
lines, but before any app.use
or app.get
lines:
app.use(express.bodyParser());
This tells Express to parse the incoming body data; if it’s JSON, then create a JSON object with it. By putting this call first, the body parsing will be called before the other route handlers. This way req.body
can be passed directly to the driver code as a JavaScript object.
Restart your Node instance once again, and execute the following command in Terminal to insert a test object into your database:
curl -H "Content-Type: application/json" -X POST -d '{"title":"Hello World"}' http://localhost:3000/items
You’ll see the record echoed back to you in your console, like so:
Now head back to your browser and reload http://localhost:3000/items
; you’ll see the item you inserted show up in the table: