PageRenderTime 61ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/_posts/2011-4-22-nodejs-restify-mongodb-mongoose.md

https://bitbucket.org/stevepict/backbonetutorials
Markdown | 289 lines | 205 code | 84 blank | 0 comment | 0 complexity | a52b5a23a7eff0d523cf9265f32cfcb6 MD5 | raw file
  1. ---
  2. layout: post
  3. title: Simple example - Node.js, Restify, MongoDb and Mongoose
  4. type: intermediate
  5. posturl: http://backbonetutorials.com/nodejs-restify-mongodb-mongoose
  6. ---
  7. # Simple example - Node.js, Restify, MongoDb and Mongoose
  8. Before I start, the Backbone.js parts of this tutorial will be using techniques described in "Organizing your application using [Modules](http://backbonetutorials.com/organizing-backbone-using-modules/) to construct a simple guestbook.
  9. ## Getting started
  10. To easily understand this tutorial you should jump straight into the example code base.
  11. [Example Codebase](https://github.com/thomasdavis/backbonetutorials/tree/gh-pages/examples/nodejs-mongodb-mongoose-restify)
  12. [Example Demo](http://backbonetutorials.com/examples/nodejs-mongodb-mongoose-restify/)
  13. This tutorial will assist you in saving data(Backbone.js Models) to MongoDb and retrieving a list(Backbone.js Collections) of them back.
  14. ## The technologies
  15. This stack is great for rapid prototyping and highly intuitive. Personal note: I love using Javascript as my only language for the entire application(FrontEnd/BackEnd/API/Database). Restify is still in early development but is essentially just an extension of Express. So for anyone needing more stability you can easily just substitute Express in.
  16. ### Node.js
  17. "Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices."
  18. ### Restify
  19. "Restify is a node.js module built specifically to enable you to build correct REST web services. It borrows heavily from express (intentionally) as that is more or less the de facto API for writing web applications on top of node.js."
  20. ### MongoDb
  21. "MongoDB (from "humongous") is a scalable, high-performance, open source NoSQL database."
  22. ### Mongoose
  23. "Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment."
  24. ## Building the server
  25. In the example repository there is a server.js example which can be executed by running `node server.js`. If you use this example in your own applications make sure to update the Backbone.js [Model](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/js/models/message.js) and [Collection](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/js/collections/messages.js) definitions to match your server address.
  26. ## Restify configuration
  27. The first thing to do is require the Restify module. Restify will be in control of handling our restFul end points and returning the appropriate JSON.
  28. {% highlight javascript %}
  29. var restify = require('restify');
  30. var server = restify.createServer();
  31. server.use(restify.bodyParser());
  32. {% endhighlight %}
  33. Note: bodyParser() takes care of turning your request data into a Javascript object on the server automatically.
  34. ## MongoDb/Mongoose configuration
  35. We simply want to require the MongoDb module and pass it a MongoDb authentication URI e.g. mongodb://username:server@mongoserver:10059/somecollection
  36. The code below presupposes you have another file in the same directory called `config.js`. Your config should never be public as it contains your credentials. So for this repository I have added `config.js` to my `.gitignore` but added in a [sample config](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/config-sample.js).
  37. {% highlight javascript %}
  38. var mongoose = require('mongoose/');
  39. var config = require('./config');
  40. db = mongoose.connect(config.creds.mongoose_auth),
  41. Schema = mongoose.Schema;
  42. {% endhighlight %}
  43. ## Mongoose Schema
  44. Mongoose introduces a concept of [model/schema](http://mongoosejs.com/docs/model-definition.html) enforcing types which allow for easier input validation etc
  45. {% highlight javascript %}
  46. // Create a schema for our data
  47. var MessageSchema = new Schema({
  48. message: String,
  49. date: Date
  50. });
  51. // Use the schema to register a model with MongoDb
  52. mongoose.model('Message', MessageSchema);
  53. var Message = mongoose.model('Message');
  54. {% endhighlight %}
  55. _Note: `Message` can now be used for all things CRUD related._
  56. ## Setting up the routes
  57. Just like in Backbone, Restify allows you to configure different routes and their associated callbacks. In the code below we want to define two routes. One for saving new messages and one for retrieving all messages. After we have created our function definitions, we then attach them to either GET/POST/PUT/DELETE on a particular restful endpoint e.g. GET /messages
  58. {% highlight javascript %}
  59. // This function is responsible for returning all entries for the Message model
  60. function getMessages(req, res, next) {
  61. // Resitify currently has a bug which doesn't allow you to set default headers
  62. // This headers comply with CORS and allow us to server our response to any origin
  63. res.header("Access-Control-Allow-Origin", "*");
  64. res.header("Access-Control-Allow-Headers", "X-Requested-With");
  65. // .find() without any arguments, will return all results
  66. // the `-1` in .sort() means descending order
  67. Message.find().sort('date', -1).execFind(function (arr,data) {
  68. res.send(data);
  69. });
  70. }
  71. function postMessage(req, res, next) {
  72. res.header("Access-Control-Allow-Origin", "*");
  73. res.header("Access-Control-Allow-Headers", "X-Requested-With");
  74. // Create a new message model, fill it up and save it to Mongodb
  75. var message = new Message();
  76. message.message = req.params.message;
  77. message.date = new Date()
  78. message.save(function () {
  79. res.send(req.body);
  80. });
  81. }
  82. // Set up our routes and start the server
  83. server.get('/messages', getMessages);
  84. server.post('/messages', postMessage);
  85. {% endhighlight %}
  86. This wraps up the server side of things, if you follow the [example](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/server.js) then you should see something like
  87. [http://backbonetutorials.nodejitsu.com/messages](http://backbonetutorials.nodejitsu.com/messages)
  88. _Note: Again you must remember to change the [Model](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/js/models/message.js) and [Collection](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/js/collections/messages.js) definitions to match your server address._
  89. ## Setting up the client(Backbone.js)
  90. I've actually used the latest copy of [http://backboneboilerplate.com](http://backboneboilerplate.com) to set up the example page.
  91. The important files you will want to check out are;
  92. * views/dashboard/page.js
  93. * views/guestbook/form.js
  94. * views/guestbook/list.js
  95. * models/message.js
  96. * collections/messages.js
  97. * templates/guestbook/
  98. ## Saving a message
  99. First of all we want to setup a [template](https://github.com/thomasdavis/backbonetutorials/blob/gh-pages/examples/nodejs-mongodb-mongoose-restify/templates/guestbook/form.html) for showing our form that creates new messages.
  100. {% highlight javascript %}
  101. <textarea class="message"></textarea>
  102. <button class="post-message">Post Message</button>
  103. {% endhighlight %}
  104. This template gets inserted into the DOM by `views/guestbook/form.js`, this Backbone view also handles the interaction of the form and the posting of the new data.
  105. Let us create a Backbone Model that has the correct url for our restFul interface.
  106. {% highlight javascript %}
  107. define([
  108. 'underscore',
  109. 'backbone'
  110. ], function(_, Backbone) {
  111. var Message = Backbone.Model.extend({
  112. url: 'http://localhost:8080/messages'
  113. });
  114. return Message;
  115. });
  116. {% endhighlight %}
  117. We can see how we require our pre-defined model for messages and also our form template.
  118. {% highlight javascript %}
  119. define([
  120. 'jquery',
  121. 'underscore',
  122. 'backbone',
  123. 'models/message',
  124. 'text!templates/guestbook/form.html'
  125. ], function($, _, Backbone, MessageModel, guestbookFormTemplate){
  126. var GuestbookForm = Backbone.View.extend({
  127. el: '.guestbook-form-container',
  128. render: function () {
  129. $(this.el).html(guestbookFormTemplate);
  130. },
  131. events: {
  132. 'click .post-message': 'postMessage'
  133. },
  134. postMessage: function() {
  135. var that = this;
  136. var message = new MessageModel();
  137. message.save({ message: $('.message').val()}, {
  138. success: function () {
  139. that.trigger('postMessage');
  140. }
  141. });
  142. }
  143. });
  144. return GuestbookForm;
  145. });
  146. {% endhighlight %}
  147. _Note: `trigger` is from Backbone Events, I binded a listener to this view in `views/dashboard/page.js` so that when a new message is submitted, the list is re-rendered. We are setting the date of post on the server so there is no need to pass it up now._
  148. ## Retrieving a list of messages
  149. We setup a route on our server to generate a list of all available messages at `GET /messages`. So we need to define a collection with the appropriate `url` to fetch this data down.
  150. {% highlight javascript %}
  151. define([
  152. 'jquery',
  153. 'underscore',
  154. 'backbone',
  155. 'models/message'
  156. ], function($, _, Backbone, MessageModel){
  157. var Messages = Backbone.Collection.extend({
  158. model: MessageModel, // Generally best practise to bring down a Model/Schema for your collection
  159. url: 'http://localhost:8080/messages'
  160. });
  161. return Messages;
  162. });
  163. {% endhighlight %}
  164. Now that we have a collection to use we can setup our `views/list.js` to require the collection and trigger a fetch. Once the fetch is complete we want to render our returned data to a template and insert it into the DOM.
  165. {% highlight javascript %}
  166. define([
  167. 'jquery',
  168. 'underscore',
  169. 'backbone',
  170. 'collections/messages',
  171. 'text!templates/guestbook/list.html'
  172. ], function($, _, Backbone, MessagesCollection, guestbookListTemplate){
  173. var GuestbookList = Backbone.View.extend({
  174. el: '.guestbook-list-container',
  175. render: function () {
  176. var that = this;
  177. var messages = new MessagesCollection();
  178. messages.fetch({
  179. success: function(messages) {
  180. $(that.el).html(_.template(guestbookListTemplate, {messages: messages.models, _:_}));
  181. }
  182. });
  183. }
  184. });
  185. return GuestbookList;
  186. });
  187. {% endhighlight %}
  188. The template file should iterate over `messages.models` which is an array and print out a HTML fragment for each model.
  189. {% highlight javascript %}
  190. <% _.each(messages, function(message) { %>
  191. <p><%= message.get('message') %></p>
  192. <em><%= message.get('date') %></em>
  193. <% }); %>
  194. {% endhighlight %}
  195. This actually sums up everything you need to know to implement this simple example.
  196. ## Conclusion
  197. [Example Codebase](https://github.com/thomasdavis/backbonetutorials/tree/gh-pages/examples/nodejs-mongodb-mongoose-restify)
  198. [Example Demo](http://backbonetutorials.com/examples/nodejs-mongodb-mongoose-restify/)
  199. In this example you should really be using relative url's in your collections/models and instead setting a baseUrl in a config file or by placing your index.html file on the restful server.
  200. This example is hosted on github therefore we had to include the absolute url to the server which is hosted on nodejitsu.com
  201. On a personal note, I have of recent used the Joyent, Nodejitsu, MongoDbHq stack after they have now partnered up and I have nothing but good things to say. Highly recommend you check it out!
  202. As always I hope I made this tutorial easy to follow!
  203. Get in touch with me on twitter, comments or github!
  204. ### Relevant Links
  205. [Organizing Your Backbone.js Application With Modules](http://weblog.bocoup.com/organizing-your-backbone-js-application-with-modules)