Vous êtes sur la page 1sur 24

03/04/14

Pixelhandler's Blog

Pixelhandler's Blog

Pushin' & pullin' pixels on the web

Develop a RESTful API Using Node.js With Express and Mongoose


by pixelhandler (2 years ago)

For the past couple months I've been developing with Backbone.js and mocking data for an application. I've worked in the ecommerce industry for a few years and thought it would be a good idea to create a serious of posts on the topic of developing with Backbone using an example with some complexity, perhaps more than a 'todos' or 'blog' application, so the example will utilize a familiar Web application, an online store. To program a data-driven asynchronous application using a language I already know, JavaScript, the best way to learn is to write some code. So, I researched a few example applications using Node.js with a MongoDB database. This article is intended to be the first in a series on the topic building an online store using REST and Backbone.js to structure the code. This tutorial is not intended for production code, but rather an exploration of developing interactions with a RESTful API. This first post lays down a foundation for developing with a local API, then I can get into using the application with Backbone; but let's get into the server-side for a bit first.

API Design for Mock Ecommerce Application


Goals for the Web service: Simple API design and pragmatic REST Web service, with only 2 base URLs per resource Keep verbs out of your base URLs Our HTTP verbs are POST, GET, PUT, and DELETE ([Create, Read, Update, Delete][crud]) Concrete names are better than abstract Example : two (2) resources ( /products and Resource
/products/XX

) and the four (4) HTTP verbs

POST GET PUT DELETE (create) (read) (update) (delete) /products create a new productlist productsbulk update products delete all products /products/1234error show 1234 if exists update 1234, else errordelete 1234

Nouns
Products There a many configurations for setting up a product to sell online, some with no options, with multiple configurable options, or groups of products. For this example I will use one of my favorite things, a t-shirt. This type of product can be configured with size and color options, e.g. Black - Large, or Red - Medium; specific material variations would be represented as separate products, like long- vs. short-sleeves. Product attributes include:

Id, Title, Description, Images: [ { Kind, URL } ], Categories: [ { Name } ], Style: Number, Varients: [ { Color, Images: [ { Kind, URL } ], Sizes: [ { Size, Available, SKU, Price } ], } ]

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

1/24

03/04/14

Pixelhandler's Blog

JSON data may be represented like so:

{ "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ], "categories": [ { "name": "Clothes" }, { "name": "Shirts" } ], "style": "1234", "variants": [ { "color": "Black", "images": [ { "kind": "thumbnail", "url": "images/products/1234/thumbnail.jpg" }, { "kind": "catalog", "url": "images/products/1234/black.jpg" } ], "sizes": [ { "size": "S", "available": 10, "sku": "CAT-1234-Blk-S", "price": 99.99 }, { "size": "M", "available": 7, "sku": "CAT-1234-Blk-M", "price": 109.99 } ] } ], "catalogs": [ { "name": "Apparel" } ] }

The above object has a variety of types composing a document that should bring up some challenges in learning how to store and update the document in a MongoDB database. The first thing I needed to do was install MongoDB (see the quickstart guide). To get to know the database I tried out using Mongo in the console. I'll need to define the Web service urls to interact with the data via REST like so... /products - list /products/:id - single

Data: MongoDB using Mongoose with Express framework running on Node.js


For installation of Node.js, NPM, and Express see: Installing Node.js npm is a package manager for node. npm install express Also there are plenty of links at the end of the article to learn about this stack.
pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose 2/24

03/04/14

Pixelhandler's Blog

Working with data using the Mongoose package in node, I will need to research storing JSON documents. The Mongoose documentation outlines the use of schema types and embedded documents; so these can be integrated into a product model to store the product JSON above. Models are defined by passing a Schema instance to mongoose.model. A Node App Running Express to Access Data using a RESTful Web Service I found that a section of 'Backbone Fundamentals' has an example application which is built using this same stack : Node.js, Express, Mongoose and MongoDB. I reviewed the example methods to GET, POST, PUT and DELETE. Then I got started with an instance of the I created a file
express.HTTPServer

app.js

and added the following JavaScript code:

var application_root = __dirname, express = require("express"), path = require("path"), mongoose = require('mongoose');

var app = express.createServer();

// Database

mongoose.connect('mongodb://localhost/ecomm_database');

// Config

app.configure(function () { app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(application_root, "public"))); app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); });

app.get('/api', function (req, res) { res.send('Ecomm API is running'); });

// Launch server

app.listen(4242);

In the above code, the line beginning with

var

loads the modules needed for the API, and


public

app = express.createServer()

creates the

Web server. The Web server can also serve static files in a
app.use(express.static(path.join(application_root, "public"))); mongoose.connect('mongodb://localhost/ecomm_database');

directory, the line in the configure block

sets up the public directory to use static files. The code,

, hooks up the database. All I needed to do is name the database, in this example I

used the name: 'ecomm_database'. With MongoDB is setup and running, the actual database is automatically generated. To run
mongod

, on the command line I needed to execute the command:

mongod run --config /usr/local/Cellar/mongodb/2.0.1-x86_64/mongod.conf

Since I installed MongoGB on OSX Lion, the above command was printed out following the installation on my MacBook. The code
app.listen(4242);

sets up the server to respond to the URL: http://localhost:4242. Once mongod is running, to start up the
node app.js

server genereated with the app.js file... I executed

on the command line.

Now, I have a folder named 'ecomapi' and inside this directory is the 'app.js' file and a directory named 'public' which has an 'index.html' file. With the static index.html file I can load the data using jQuery which I am linking to on a CDN. Later I will be able to try out AJAX calls to create products using my browser's JavaScript console.

ecomapi |-- app.js

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

3/24

03/04/14
`-- public `-- index.html

Pixelhandler's Blog

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>API index</title> </head> <body> <section> <h1>Nouns...</h1> <p> /products<br> /products/:id </p> </section> <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> </body> </html>

In my browser I can load http://localhost:4242 and see that the static index.html file loads and also hitting http://localhost:4242/api spits out some text 'Ecomm API is running', which is the result of the get response:

app.get('/api', function (req, res) { res.send('Ecomm API is running'); });

Up to this point we have a simple server with a single get response, next I can add in the data models and REST services. Setup a Simple Model Using CRUD (create, read, update, delete) Following the
app.configure

code block, in the app.js, I added a Schema and a Product Model:

var Schema = mongoose.Schema;

var Product = new Schema({ title: { type: String, required: true }, description: { type: String, required: true }, style: { type: String, unique: true }, modified: { type: Date, default: Date.now } });

Since I still needed to learn how to use the schema types and embedded documents that come with mongoose, I didn't add the price yet which should be set on a combination of color and size. To use the model I created a variable
ProductModel

var ProductModel = mongoose.model('Product', Product);

Now I can add the CRUD methods:


READ a List of Products

app.get('/api/products', function (req, res){ return ProductModel.find(function (err, products) { if (!err) { return res.send(products); } else { return console.log(err); } }); });

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

4/24

03/04/14

Pixelhandler's Blog

CREATE a Single Product

app.post('/api/products', function (req, res){ var product; console.log("POST: "); console.log(req.body); product = new ProductModel({ title: req.body.title, description: req.body.description, style: req.body.style, }); product.save(function (err) { if (!err) { return console.log("created"); } else { return console.log(err); } }); return res.send(product); });

READ a Single Product by ID

app.get('/api/products/:id', function (req, res){ return ProductModel.findById(req.params.id, function (err, product) { if (!err) { return res.send(product); } else { return console.log(err); } }); });

UPDATE a Single Product by ID

app.put('/api/products/:id', function (req, res){ return ProductModel.findById(req.params.id, function (err, product) { product.title = req.body.title; product.description = req.body.description; product.style = req.body.style; return product.save(function (err) { if (!err) { console.log("updated"); } else { console.log(err); } return res.send(product); }); }); });

DELETE a Single Product by ID

app.delete('/api/products/:id', function (req, res){ return ProductModel.findById(req.params.id, function (err, product) { return product.remove(function (err) { if (!err) { console.log("removed"); return res.send(''); } else { console.log(err); }

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

5/24

03/04/14
}); }); });

Pixelhandler's Blog

NOTE: To exit your running app.js job, press app.js

control-c

then re-start your updated app.js using the same command as before: node

With the new product model and CRUD methods serving up a RESTful service at http://localhost:4242/api I can utilize the index.html (with jQuery)... and in my browser's console I can fiddle with my new Web service using jQuery's AJAX methods. Specifically, by loading http://localhost:4242/ and executing commands in the JavaScript console I can using ($.ajax) POST to create a new product.

jQuery.post("/api/products", { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "style": "12345" }, function (data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

The post response is something like:

_id: "4f34d8e7f05ebf212b000004" description: "All about the details. Of course it's black." modified: "2012-02-10T08:44:23.372Z" style: "12345" title: "My Awesome T-shirt"

The

_id

property was added automatically, this value can be used to UPDATE, READ, or DELETE the record. Notice all the and
console.dir()

console.log()

calls I added within the anonymous functions' 'success' callbacks. With the logging in place, I can

inspect the server's response in the console or by viewing the responses on in network tab of my browser's developer tools. To READ the product data I just created, I execute the following code in my browser's JavaScript console:

jQuery.get("/api/products/", function (data, textStatus, jqXHR) { console.log("Get resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

The above GET request reads all products; to read a specific product add the ID to the URL like so:

jQuery.get("/api/products/4f34d8e7f05ebf212b000004", function(data, textStatus, jqXHR) { console.log("Get resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

To test the UPDATE request use PUT:

jQuery.ajax({ url: "/api/products/4f34d8e7f05ebf212b000004", type: "PUT", data: { "title": "My Awesome T-shirt in Black", "description": "All about the details. Of course it's black, and long sleeve", "style": "12345"

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

6/24

03/04/14
}, success: function (data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); } });

Pixelhandler's Blog

The above code is about the same as the the previous code I used to create the product document and store in MongoDB. However, I appended the product's description with the text: 'black, and long sleeve'. Now, when I get the product by ID, I see the updated text added to the product description:

jQuery.get("/api/products/4f34d8e7f05ebf212b000004");

Or I can visit : http://localhost:4242/api/products/4f34d8e7f05ebf212b000004 to see the text response only. I can also DELETE the product:

jQuery.ajax({ url: "/api/products/4f34d8e7f05ebf212b000004", type: "DELETE", success: function (data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); } });

Now when I load http://localhost:4242/api/products/4f34d8e7f05ebf212b000004 the server response with a

null

response

TIP: I am using a log of console.log() and console.dir() calls within the success (anonymous) functions to view the responses from the server. Embedded Documents for the Remaining Product Attributes I am now adding a few items to the product model: images, categories, catalogs, variants. A t-shirt product may have many variants with size and color options; the pricing should be configured by the combination of: the selected size option which belongs to a selected color option. The product may belong one or more product catalogs, and also should have one or more associated categories.

// Product Model

var Product = new Schema({ title: { type: String, required: true }, description: { type: String, required: true }, style: { type: String, unique: true }, images: [Images], categories: [Categories], catalogs: [Catalogs], variants: [Variants], modified: { type: Date, default: Date.now } });

The embedded documents are in square brackets (above) in the product model. I referenced the Mongoose documentation to learn how to assemble this model with embedded documents. Below are the schema assignments that together assemble the product document to store in MongoDB. My strategy is adding one embedded document at time and updating each the CREATE and UPDATE methods, stopping and restarting the application
pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose 7/24

03/04/14

Pixelhandler's Blog
node app.js

( control-c then

) with each iteration. And working out the additional code by fiddling with the same jQuery
post

$.ajax

requests, but also adding the single attribute(s) added to the

data to create a new product document in the db.

// Schemas

var Sizes = new Schema({ size: { type: String, required: true }, available: { type: Number, required: true, min: 0, max: 1000 }, sku: { type: String, required: true, validate: [/[a-zA-Z0-9]/, 'Product sku should only have letters and numbers'] }, price: { type: Number, required: true, min: 0 } });

var Images = new Schema({ kind: { type: String, enum: ['thumbnail', 'catalog', 'detail', 'zoom'], required: true }, url: { type: String, required: true } });

var Variants = new Schema({ color: String, images: [Images], sizes: [Sizes] });

var Categories = new Schema({ name: String });

var Catalogs = new Schema({ name: String });

For example, I first added the


kind

[Images]

embedded docuemnt to my product model and tested out the application by updated the

AJAX post which creates the product using the same post as before but with an array of objects with the image attributes for and
url

, see below:

var Images = new Schema({ kind: String, url: String });

var Product = new Schema({ title: { type: String, required: true }, description: { type: String, required: true }, style: { type: String, unique: true }, images: [Images], modified: { type: Date, default: Date.now } });

I also updated the CREATE (POST) and UPDATE (PUT) methods, adding the references to the images attribute (an embedded document) of the product model.

// CREATE a product

app.post('/api/products', function(req, res){ var product; console.log("POST: "); console.log(req.body);

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

8/24

03/04/14
product = new ProductModel({ title: req.body.title, description: req.body.description, style: req.body.style, images: [Images] }); product.save(function(err) { if (!err) { return console.log("created"); } else { return console.log(err); } }); return res.send(product); });

Pixelhandler's Blog

// UPDATE a single product

app.put('/api/products/:id', function(req, res){ return ProductModel.findById(req.params.id, function(err, product) { product.title = req.body.title; product.description = req.body.description; product.style = req.body.style; product.images = req.body.images; return product.save(function(err) { if (!err) { console.log("updated"); } else { console.log(err); } return res.send(product); }); }); });

Then I worked out the adding the image(s) data to my post that creates a product in the database; addming the images data array with an object like so:

jQuery.post("/api/products", { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "style": "1234", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ] }, function(data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

On my first of attempt of adding multiple documents to the product model, I did get errors and the server's create action failed. However, my terminal (shell) does report the errors - the app.js file uses the code
showStack: true })); app.use(express.errorHandler({ dumpExceptions: true,

to setup the display of errors on the command line. Also, I added some console.log calls in the post action to

log the request pluse notes on the execution of saving the document. On both the browser and on the command line, all the logging indicates whether I am building the product model (using Mongoose) properly. This attempt to add the images did not save. I am not sure why, but I switched over to adding a [Categories] embedded docuemnt, then worked my way toward a completed product model with an API to CREATE, UPDATE and DELETE a single product at a time and to READ a single product or list of products in an array. After debugging the embedded documents I added for the product models attribtues... now I can create the complete product in a post:
pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose 9/24

03/04/14
jQuery.post("/api/products", { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ], "categories": [ { "name": "Clothes" }, { "name": "Shirts" } ], "style": "1234", "variants": [ { "color": "Black", "images": [ { "kind": "thumbnail", "url": "images/products/1234/thumbnail.jpg" }, { "kind": "catalog", "url": "images/products/1234/black.jpg" } ], "sizes": [ { "size": "S", "available": 10, "sku": "CAT-1234-Blk-S", "price": 99.99 }, { "size": "M", "available": 7, "sku": "CAT-1234-Blk-M", "price": 109.99 } ] } ], "catalogs": [ { "name": "Apparel" } ] }, function(data, textStatus, jqXHR) {

Pixelhandler's Blog

console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

And from the node console (shell) I get this output:

POST: { title: 'My Awesome T-shirt', description: 'All about the details. Of course it\'s black.', images: [ { kind: 'thumbnail', url: 'images/products/1234/main.jpg' } ], categories: [ { name: 'Clothes' }, { name: 'Shirts' } ], style: '1234', varients: [ { color: 'Black', images: [Object], sizes: [Object] } ], catalogs: [ { name: 'Apparel' } ] } validate style 1234 validate description All about the details. Of course it's black. validate title My Awesome T-shirt created

In my browser this looks like these two screenshot:


pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose 10/24

03/04/14

Pixelhandler's Blog

Ready to post using the console

Server response in the network tab

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

11/24

03/04/14

Pixelhandler's Blog

Bulk Actions for UPDATE and DELETE Finally to finish up the actions needed in the design for the products web service I added the bulk actions to remove all products at once and also to update many products in a PUT request.

app.delete('/api/products', function (req, res) { ProductModel.remove(function (err) { if (!err) { console.log("removed"); return res.send(''); } else { console.log(err); } }); });

app.put('/api/products', function (req, res) { var i, len = 0; console.log("is Array req.body.products"); console.log(Array.isArray(req.body.products)); console.log("PUT: (products)"); console.log(req.body.products); if (Array.isArray(req.body.products)) { len = req.body.products.length; } for (i = 0; i < len; i++) {

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

12/24

03/04/14
console.log("UPDATE product by id:"); for (var id in req.body.products[i]) { console.log(id); }

Pixelhandler's Blog

ProductModel.update({ "_id": id }, req.body.products[i][id], function (err, numAffected) { if (err) { console.log("Error on update"); console.log(err); } else { console.log("updated num: " + numAffected); } }); } return res.send(req.body.products); });

See the Gist links that follow for sample scripts to create many products (fixtures) and also the bulk update with single AJAX PUT request.

The app.js, index.html and jQuery AJAX snippets developed in this tutorial
The Source Code for This Tutorial is on GitHub as a Gist: Develop a RESTful API Using Node.js With Express and Mongoose Fixtures - example AJAX posts to create products Sample script for bulk update of products The application file:

var application_root = __dirname, express = require("express"), path = require("path"), mongoose = require('mongoose');

var app = express.createServer();

// database

mongoose.connect('mongodb://localhost/ecomm_database');

// config

app.configure(function () { app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(application_root, "public"))); app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); });

var Schema = mongoose.Schema; //Schema.ObjectId

// Schemas

var Sizes = new Schema({ size: { type: String, required: true }, available: { type: Number, required: true, min: 0, max: 1000 }, sku: { type: String, required: true, validate: [/[a-zA-Z0-9]/, 'Product sku should only have letters and numbers'] }, price: { type: Number, required: true, min: 0 } });

var Images = new Schema({ kind: { type: String, enum: ['thumbnail', 'catalog', 'detail', 'zoom'],

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

13/24

03/04/14
required: true }, url: { type: String, required: true } });

Pixelhandler's Blog

var Variants = new Schema({ color: String, images: [Images], sizes: [Sizes] });

var Categories = new Schema({ name: String });

var Catalogs = new Schema({ name: String });

// Product Model

var Product = new Schema({ title: { type: String, required: true }, description: { type: String, required: true }, style: { type: String, unique: true }, images: [Images], categories: [Categories], catalogs: [Catalogs], variants: [Variants], modified: { type: Date, default: Date.now } });

// validation

Product.path('title').validate(function (v) { console.log("validate title"); console.log(v); return v.length > 10 && v.length < 70; });

Product.path('style').validate(function (v) { console.log("validate style"); console.log(v); return v.length < 40; }, 'Product style attribute is should be less than 40 characters');

Product.path('description').validate(function (v) { console.log("validate description"); console.log(v); return v.length > 10; }, 'Product description should be more than 10 characters');

var ProductModel = mongoose.model('Product', Product);

/* Product Document [ { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ], "categories": [ { "name": "Clothes" }, { "name": "Shirts" } ], "style": "1234", "variants": [ { "color": "Black", "images": [

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

14/24

03/04/14
{ "kind": "thumbnail", "url": "images/products/1234/thumbnail.jpg" }, { "kind": "catalog", "url": "images/products/1234/black.jpg" } ], "sizes": [ { "size": "S", "available": 10, "sku": "CAT-1234-Blk-S", "price": 99.99 }, { "size": "M", "available": 7, "sku": "CAT-1234-Blk-M", "price": 109.99 } ] } ], "catalogs": [ { "name": "Apparel" } ] } ] */

Pixelhandler's Blog

// REST api

app.get('/api', function (req, res) { res.send('Ecomm API is running'); });

// POST to CREATE app.post('/api/products', function (req, res) { var product; console.log("POST: "); console.log(req.body); product = new ProductModel({ title: req.body.title, description: req.body.description, style: req.body.style, images: req.body.images, categories: req.body.categories, catalogs: req.body.catalogs, variants: req.body.variants }); product.save(function (err) { if (!err) { return console.log("created"); } else { return console.log(err); } }); return res.send(product); });

// PUT to UPDATE

// Bulk update app.put('/api/products', function (req, res) { var i, len = 0; console.log("is Array req.body.products"); console.log(Array.isArray(req.body.products)); console.log("PUT: (products)"); console.log(req.body.products); if (Array.isArray(req.body.products)) { len = req.body.products.length;

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

15/24

03/04/14
} for (i = 0; i < len; i++) { console.log("UPDATE product by id:"); for (var id in req.body.products[i]) { console.log(id); }

Pixelhandler's Blog

ProductModel.update({ "_id": id }, req.body.products[i][id], function (err, numAffected) { if (err) { console.log("Error on update"); console.log(err); } else { console.log("updated num: " + numAffected); } }); } return res.send(req.body.products); });

// Single update app.put('/api/products/:id', function (req, res) { return ProductModel.findById(req.params.id, function (err, product) { product.title = req.body.title; product.description = req.body.description; product.style = req.body.style; product.images = req.body.images; product.categories = req.body.categories; product.catalogs = req.body.catalogs; product.variants = req.body.variants; return product.save(function (err) { if (!err) { console.log("updated"); } else { console.log(err); } return res.send(product); }); }); });

// GET to READ

// List products app.get('/api/products', function (req, res) { return ProductModel.find(function (err, products) { if (!err) { return res.send(products); } else { return console.log(err); } }); });

// Single product app.get('/api/products/:id', function (req, res) { return ProductModel.findById(req.params.id, function (err, product) { if (!err) { return res.send(product); } else { return console.log(err); } }); });

// DELETE to DESTROY

// Bulk destroy all products app.delete('/api/products', function (req, res) { ProductModel.remove(function (err) { if (!err) { console.log("removed"); return res.send(''); } else { console.log(err); }

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

16/24

03/04/14
}); });

Pixelhandler's Blog

// remove a single product app.delete('/api/products/:id', function (req, res) { return ProductModel.findById(req.params.id, function (err, product) { return product.remove(function (err) { if (!err) { console.log("removed"); return res.send(''); } else { console.log(err); } }); }); });

// launch server

app.listen(4242);

Also in the app.js gist (above), I added code to validate the product model using Mongoose. The index file (inside /public directory):

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>API index</title> </head> <body> <section> <h1>Nouns...</h1> <p> /products<br> /products/:id </p> </section> <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> </body> </html>

Some jQuery AJAX snippets to fiddle with the API:

// jQuery snippets used in the console to use the REST api created with app.js

// CREATE

jQuery.post("/api/products", { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black.", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ], "categories": [ { "name": "Clothes" }, { "name": "Shirts" } ], "style": "1234", "variants": [ { "color": "Black", "images": [ { "kind": "thumbnail",

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

17/24

03/04/14
"url": "images/products/1234/thumbnail.jpg" }, { "kind": "catalog", "url": "images/products/1234/black.jpg" } ], "sizes": [ { "size": "S", "available": 10, "sku": "CAT-1234-Blk-S", "price": 99.99 }, { "size": "M", "available": 7, "sku": "CAT-1234-Blk-M", "price": 109.99 } ] } ], "catalogs": [ { "name": "Apparel" } ] }, function(data, textStatus, jqXHR) {

Pixelhandler's Blog

console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

// generated a product document with automatically assigned ID, e.g. 4f34734d21289c1c28000007

// READ

jQuery.get("/api/products/", function(data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

jQuery.get("/api/products/4f34734d21289c1c28000007", function(data, textStatus, jqXHR) { console.log("Post resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); });

// UPDATE

jQuery.ajax({ url: "/api/products/4f34734d21289c1c28000007", type: "PUT", data: { "title": "My Awesome T-shirt", "description": "All about the details. Of course it's black, and longsleeve.", "images": [ { "kind": "thumbnail", "url": "images/products/1234/main.jpg" } ], "categories": [ { "name": "Clothes" }, { "name": "Shirts" } ], "style": "1234", "variants": [ { "color": "Black", "images": [ { "kind": "zoom",

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

18/24

03/04/14
"url": "images/products/1234/zoom.jpg" } ], "sizes": [ { "size": "L", "available": 77, "sku": "CAT-1234-Blk-L", "price": 99.99 } ] } ], "catalogs": [ { "name": "Apparel" } ] }, success: function(data, textStatus, jqXHR) { console.log("PUT resposne:"); console.dir(data); console.log(textStatus); console.dir(jqXHR); } });

Pixelhandler's Blog

// Delete

jQuery.ajax({url: "/api/products/4f34734d21289c1c28000007", type: "DELETE", success: function(data, textStatus, jqXHR) { console.dir(data); }});

Post Hoc
This tutorial came about as a desire to develop with a local API. Using a local API, I can develop a client application with Backbone.js and utilize the asynchronous behaviors that come with the API. I am not suggesting that anyone uses this tutorial to build a RESTful API for a production ecommerce application. However, I do advocate developing with a local API rather then just mocking a server without asynchronous interations with JSON data. If you are not working with a RESTful API and are not consuming data using AJAX, in a few hours you can be. JavaScript runs in so many applications, and since I already know JavaScript I would rather fiddle with Node.js than build an API for my local development needs in PHP or Ruby. Also, this exercise helps me to understand more about JSON, REST and jQuery AJAX development. Getting to know these technologies and developing solid skills using asynchronous behavior, necessary to build HTML5 apps for desktop and/or mobile browsers. Completing this tutorial will likely take a few hours, even longer if you do not have node and npm running on your development environment.

Reference
API design nouns are good, verbs are bad. Installing Node.js npm is a package manager for node. Models are defined by passing a Schema instance to mongoose.model SchemaTypes take care of validation, casting, defaults, and other general options in our models Embedded documents are documents with schemas of their own that are part of other documents Backbone Todo boilerplates demonstrating integration with Node.js, Express, MongoDB MongoDB (from 'humongous') - Document-oriented storage MongoDB Quickstart Try manipulating the Mongo database with the database shell or MongoDB browser shell Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. High performance, high class Web development for Node.js npm install express Using Node.js, Express, Mongoose and MongoDB
53 Comments Pixelhandler's Blog pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose Login

19/24

03/04/14 53 Comments
Sort by Best

Pixelhandler's Blog

Pixelhandler's Blog
Share

Login
Favorite

Join the discussion


Andrew 14
a year ago

Can someone tell me why it would be bad to use this as production code and what needs to be added to make it acceptable?
Reply Share

Matt Vleming

Andrew 3 months ago

I too would like to hear these two questions answered. Is there a guide someone can recommend for securing APIs built w/ Node.JS for production?
Reply Share

Ben Clayton

a year ago

Note that (at least with the current version of express) to route an HTTP delete call you use app.del("URL", function) NOT app.delete("URL", function) as the code examples above say. 'delete' is a reserved word in JavaScript.
9
Reply Share
a year ago

Glorydeath

This is very useful! Thank you! I'm trying to develop a restful API using nodejs. And, apart from json, I would also serialize it into xml and rdf. Can you please tell me how to achieve those? Thanks.
3 zgollum
Reply Share
a year ago

Are you leaving your clients hanging and waiting for response instead of sending a proper 404 in case no document is found in your get handler?
4 jsmarkus
Reply Share
2 years ago

Nice post, thank you! Some months ago I developed a RESTful application like this. But later on - the more resources it served, the more boring it became to write code. So I decided to write RESTful framework that simplifies developing such things. Meet: https://github.com/jsmarkus/co... It is still under development, but new parts of my application are written with it. And I believe the Colibri project will grow, because it is not a just-for-fun thing. I encourage you guys to join me in open-source development of Colibri framework :)
1
Reply Share
pixelhandler 2 years ago

Bill Heaton

Today I deployed this ecomapi to Heroku at : http://ecomapi.herokuapp.com/ the snippets for using the API are here https://gist.github.com/179108... ; to see a list of products in the mongo db generated with Backbone.js code add #list http://ecomapi.herokuapp.com/#... to work with the api use the javascript console and the code snippets in the gists.
1
Reply Share

Patrick J. Jankun

Bill Heaton a year ago

ooh. I missed that it's a bulk action ;)


Reply Share

Xiaohero1990

2 years ago

Hi,I want ask a question.If I want to use 'express',I must run "express newApp".It just product a lot of useless files.
Reply Share

lewdaly

4 months ago

Thanks so much! Excellent post.


Reply Share

LH

8 months ago

Thank you for the excellent post


Reply Share

Ado Trakic

11 months ago

Really nice tutorial - thanks for putting this together. Executed parts of it, hopefully will do the whole tutorial. How are you planning to handle developers access to your APIs, tracking usage, impose limits etc.?

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

20/24

03/04/14

Pixelhandler's Blog
Is there another tool that can serve this purpose or we need to use API Management platforms like Mashery, Apigee, Apiphany etc.? Thanks. ~ Ado about.me/adotrakic
Reply Share

Hugo Dias

11 months ago

Great post man. Im using some lines of your code to build my own Node + Mongo + AngularJS App. Thank you so much
Reply Share

winered

11 months ago

so cool, thanks a lot!


Reply Share

lricoy

a year ago

One thing thought, on the line 10 of create.js you use images: [Images] instead of req.body.images;
Reply Share

Ben Duncan

a year ago

This is still a great tutorial for building RESTful APIs for Node. It's got some real good fundamentals in here and goes into realistic detail about stuff you need to worry about as far as Node specifics and basics, as well as those of Express. Spectacular job, thanks!
Reply Share

Andrew

a year ago

To edit and delete actions I added: if(product === undefined) return res.send(404); right after the findById call to deal with invalid product calls and for get all and get one product calls I modified return console.log(err)' to console.log(err); return res.send(404); to deal with invalid product calls.
Reply Share

pmjtoca

a year ago

Hi. I am reading a lot of tech. docs and tuts as a soft.quality auditor... Just to say that your tuts. is excellent in terms of pedagogy. Vraiment Bravo!!
Reply Share

alfared

a year ago

Thanks for the very good article. It helped me to understand the principles and methods of REST.
Reply Share

ShloopyD

a year ago

Have you tried putting the routers into their own files?
Reply Share

Simon

a year ago

Excellent post. Thank you for all the details!


Reply Share

Oliver Fischer

a year ago

Awesome stuff, really helpful for coding...


Reply Share

Joel Kang

a year ago

So many hours saved because of this tutorial. Thanks so much!


Reply Share

Suvi Vignarajah

a year ago

awesome blog entry with a thorough tutorial of how node, mongoose and mongodb all tie in together
Reply Share

marcoslhc

a year ago

I'm new in this node.js express thing. This is the most useful article in the subject I've found. Thanks pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

21/24

03/04/14

Pixelhandler's Blog I'm new in this node.js express thing. This is the most useful article in the subject I've found. Thanks
Reply Share

sean

a year ago

Great post, thanks


Reply Share

pixelBender67

a year ago

Wow, this is a great article, thanks so much!


Reply Share

Denis O'Sullivan

a year ago

very well written Bill, clearly you put a lot of effort into this, lots for me to learn, thank you
Reply Share

2 years ago

really liked the link because reall effort has been put on this link to make it successful.
Reply Share

Utuxia Consulting

2 years ago

Great to see some new energy in the node community w/ respect to tutorials.
Reply Share

nzru

2 years ago

Great post!
Reply Share

Evan

2 years ago

Nice post! very helpful!


Reply Share

Andy

2 years ago

very ql!
Reply Share

Sergey Romanov

2 years ago

This is excellent tutorial. I have got only one problem Warning: express.createServer() is deprecated, expressapplications no longer inherit from http.Server,please use: var express = require("express"); var app = express(); What do I do? Can you update your tutorial according to new changes? I need it very much.
Reply Share

Adam Gibbons

Sergey Romanov a year ago

Just replace: var app = express.createServer(); with this: var app = express();
Reply Share

Brad Proctor

2 years ago

Excellent tutorial. This is exact what I've been looking for.


Reply Share

prasadgc

2 years ago

Hi, thanks for this. It's a great tutorial. However, the final version of app.js in your Gist doesn't run. It fails with the following error: events.js:66 throw arguments[1]; // Unhandled 'error' event ^ Error: listen EADDRINUSE at errnoException (net.js:781:11) at Server._listen2._connectionKey (net.js:922:26) at process.startup.processNextTick.process._tickCallback (node.js:244:9) What am I missing?
Reply Share

Bill Heaton

pixelhandler

prasadgc 2 years ago

Did you look at the source found here: https://github.com/pixelhandle... this is the repository for the working demo on heroku: http://ecomapi.herokuapp.com/ pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

22/24

03/04/14
http://ecomapi.herokuapp.com/
Reply Share

Pixelhandler's Blog

Donald Pipowitch

2 years ago

Thank you very much. Your post was an enormous help for me.
Reply Share

UnInvitado

2 years ago

thank you very much for this post! very good introduction for combining different technologies. I would recommend you to update this tutorial using socket.io, and in particular, backbone.iobin, a project that simply override the .sync libraries with socket.io. awesome. thanks again!
Reply Share

Gregory

UnInvitado 2 years ago

How would you change to use socket.io? REST over Ajax seems to work.
Reply Share

Edmond Lau

2 years ago

This is very useful. Thanks you!


Reply Share

Vance Lucas

2 years ago

Great tutorial. You might want to check out Frisby.js ( http://frisbyjs.com ) for your API tests though instead of using jQuery - it's a bit cleaner, and can be eventually integrated with CI tools when the code goes to production.
Reply Share

imjp

2 years ago

You have no idea how thankful I am for this post! I've been looking like crazy for a "decent" one and this one just simply blows all of the other posts out of the water :D awesome post!
Reply Share

Mike

2 years ago

Great post! I have a very similar API set up but I'm wondering how to handle a GET when instead of an embedded document you store an array of ObjectIDs (linking)? This would probably require a 2nd GET, correct?
Reply Share

myousufq

2 years ago

Nice post. thanks


Reply Share

Bill Heaton

pixelhandler

2 years ago

I updated this post adding the code to app.js for the bulk actions for UPDATE and DELETE. Also added to the gist code a fixtures.js file to use to create products following a bulk delete, and a bulk-updates.js script to for AJAX PUT request to update multiple products. Also added to the gist is Backbone.js code to render a list of the products using this API, see: https://gist.github.com/179108...
Reply Share

Robin Dang

Bill Heaton 7 months ago

I tested it with delete command with id parameter and it called the one that deletes all?
Reply Share

Bill Heaton

pixelhandler

Robin Dang 7 months ago

I tried out the demo code running here: http://ecomapi.herokuapp.com/#... and was able to choose an `_id` then delete just that id. It's likely that I need to update this tutorial it's been about a year and a half since I posted it.
Reply Share

Ash

2 years ago

Really helpful tutorial. I'm messing around with MongoDB right now and trying to decide which node.js module to use in combination with it. So this tutorial is perfectly timed! Thanks.
Reply Share

Load more comments

ALSO ON PIXELHANDLER'S BLOG

WHAT'S THIS?

Scaffold for a browser app built with Ember.js, Rails, Ember.Data


3 comments a month ago

Using Ember.StateManager: Example Apps


1 comment 8 months ago

E rik S undell thanks for this =) // consideRatio @ irc

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

23/24

03/04/14
Jeffrey B iles Thanks for putting this together! Even though I've

Pixelhandler's Blog
been doing ember for a while, this is a great checklist for when I start a new project or add a new

E rik S undell thanks for this =) // consideRatio @ irc

Create a Custom Select Box using Ember.Component


1 comment a month ago

Pixelhandler's Blog
2 comments a month ago

A bhay a Thapa Good Read. Is there any way we can replace

aevo The response time and rendering speed are amazing.

select with ul li and fake it as select box ?? We all know there is huge restriction when it comes to

Good job.

Subscribe

Add Disqus to your site

Recent Posts
We are EmberConf 2014 End-to-end Javascript Testing: Integration Tests Using Ember.js Test Helpers Refreshed my Blog with Express and Ember.js Scaffold for a browser app built with Ember.js, Rails, Ember.Data Create a Custom Select Box using Ember.Component Testing an Ember Application: Integration and Unit tests Using Ember.StateManager: Example Apps Bowling Game Kata Using Mocha (BDD) Test Framework and Yeoman Backbone.js Models, Views and Collections to Present API Data Develop a RESTful API Using Node.js With Express and Mongoose

Links
Blog Archive About

The End. [Personal blog, Copyright @2014 Bill Heaton] | Admin

pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose

24/24

Vous aimerez peut-être aussi