Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

OSCON 2011 CouchApps

8,829 views

Published on

CouchApps are web applications built using CouchDB, JavaScript, and HTML5. CouchDB is a document-oriented database that stores JSON documents, has a RESTful HTTP API, and is queried using map/reduce views. This talk will answer your basic questions about CouchDB, but will focus on building CouchApps and related tools.

Published in: Technology
  • Be the first to comment

OSCON 2011 CouchApps

  1. 1. += CouchApps Blog: http://bradley-holt.com@BradleyHolt (http://twitter.com/BradleyHolt) bradley.holt@foundline.com
  2. 2. About Me(Bradley Holt)
  3. 3. Co-Founder andTechnical Director
  4. 4. from Vermont Battery Park - Burlington, Vermont by Marty Desilets, on Flickr
  5. 5. Organizer BTV
  6. 6. (Minor) Contributor
  7. 7. Author http://oreilly.com/catalog/9781449303129/ http://oreilly.com/catalog/9781449303433/
  8. 8. Why CouchDB?
  9. 9. CouchDB is a database,web server & application server
  10. 10. Bene tsSchema-less “self-contained” JSON documents—no tables, just documentsRESTful HTTP API is simple to integrate withCustom-written MapReduce “views” index your data for fast retrievalMulti-master replication can happen continuously, in any direction, and/orwhenever nodes can connectCan run on anything from a server to a mobile device (CouchDB can be embeddedin Android and iOS apps)
  11. 11. Trade-OffsNo ad-hoc queries (you can use temporary views, but they’re slow onlarge data sets)Thinking in terms of MapReduce views can take some time to get used toNo transactions across document boundariesYour data will be “eventually consistent” and con icts can occur (butcan be resolved)
  12. 12. MapReduce Views
  13. 13. Mapping Document Titlesfunction(doc) { // JSON object representing a doc to be mapped if (doc.title) { // make sure this doc has a title emit(doc.title); // emit the doc’s title as the key }}
  14. 14. Mapped Document Titles key id value"Building iPhone Apps with "978-0-596-80579-1" nullHTML, CSS, and JavaScript""CouchDB: The Definitive "978-0-596-15589-6" null Guide""DocBook: The Definitive "978-1-565-92580-9" null Guide" "RESTful Web Services" "978-0-596-52926-0" null
  15. 15. Mapping Document Authorsfunction(doc) { // JSON object representing a doc to be mapped if (doc.authors) { // make sure this doc has an authors eld for (var i in doc.authors) { emit(doc.authors[i]); // emit each author as the key } }}
  16. 16. Mapped Authors key id value "J. Chris Anderson" "978-0-596-15589-6" null "Jan Lehnardt" "978-0-596-15589-6" null "Jonathan Stark" "978-0-596-80579-1" null "Leonard Muellner" "978-1-565-92580-9" null"Leonard Richardson" "978-0-596-52926-0" null "Noah Slater" "978-0-596-15589-6" null "Norman Walsh" "978-1-565-92580-9" null "Sam Ruby" "978-0-596-52926-0" null
  17. 17. HTTP APIvia the CouchDB jQuery Pluginhttp://localhost:5984/_utils/script/jquery.couch.js
  18. 18. The same-origin policy dictates that your HTMLand AJAX data (JSON) must be retrieved fromthe same origin.
  19. 19. Create Database$.couch.db("mydb").create({ success: function(data) { console.log(data); }});PUT http://localhost:5984/mydb 201 Created{"ok":true}
  20. 20. Save New Document$.couch.db("mydb").saveDoc({}, { success: function(data) { console.log(data); }});POST http://localhost:5984/mydb 201 Created{ "ok":true, "id":"e15d848571c8a0352e94738ba6018790", "rev":"1-967a00dff5e02add41819138abb3284d"}
  21. 21. Save Updated Documentvar doc = { _id: "0a72c9c36bd169818dc97ed18b000aa4", _rev: "1-967a00dff5e02add41819138abb3284d", title: "CouchApps"};$.couch.db("mydb").saveDoc(doc, { success: function(data) { console.log(data); }});
  22. 22. Save Updated Document (cont’d)PUT http://localhost:5984/mydb/0a72c9c36bd169818dc97ed18b000aa4 201 Created{ "ok":true, "id":"0a72c9c36bd169818dc97ed18b000aa4", "rev":"2-516027e3179a22a22e06874c374e8ef0"}
  23. 23. Remove Documentvar doc = { _id: "0a72c9c36bd169818dc97ed18b000aa4", _rev: "2-516027e3179a22a22e06874c374e8ef0"};$.couch.db("mydb").removeDoc(doc, { success: function(data) { console.log(data); }});
  24. 24. Remove Document (cont’d)DELETE http://localhost:5984/mydb/0a72c9c36bd169818dc97ed18b000aa4?rev=2-516027e3179a22a22e06874c374e8ef0 200 OK{ "id":0a72c9c36bd169818dc97ed18b000aa4 "ok":true, "rev":"3-e9a5aa1c486eee23c84fa028bc904991"}
  25. 25. Changes (Long Polling)$.couch.db("mydb").changes().onChange(function(data) { console.log(data);});GET http://localhost:5984/mydb/_changes?heartbeat=10000&feed=longpoll&since=34 200 OK{ "results":[ { "seq":35, "id":"d12ee5ea1df6baa2b06451f44a01b7b5", "changes":[ { "rev":"1-967a00dff5e02add41819138abb3284d" } ] } ], "last_seq":35}
  26. 26. Why CouchApps?
  27. 27. Bene tsStreamlining of your codebase (no middle tier)Same language on both the client and server (JavaScript)Show and list functions let you generate HTML from within CouchDB, if you don’twant to rely on JavaScriptReplication of both data and code togetherDeploy/replicate an application along with its dataGoes “with the grain” of the web
  28. 28. “Ground Computing”Replication lters allow you to replicate relevant data to a userLocal data means faster access to the user’s dataOffline accessData portabilityDecentralization—no need for a canonical databasePotentially gives more control to the user over his or her own data
  29. 29. What about the HTML5Web Storage API?Allows persistent storage of key/value pairsEnjoys signi cant cross-browser supportLacks indexed queriesNo replication featuresIndexedDB might help, but is not part of the HTML5 speci cation and is onlyimplemented in a limited number of browsersNeed to look outside the HTML5 speci cation if you need more than just a key/value storage
  30. 30. Trade-OffsTooling could use some improvementTerminology still unclear—CouchApp can mean many different thingsMust use JavaScript for the presentation tierPure JavaScript applications may have SEO issuesShow and list functions must be side-effect free, which can be limiting
  31. 31. UsesMobile applications that require offline accessMulti-device applications (e.g. address book, tasks)Peer-to-peer collaboration applicationsDistributed social networkingAny application that stores “documents”—CMS, wiki, etc.Geospatial applications (via GeoCouch)
  32. 32. }MustacheLogic-Less Templates
  33. 33. Variables
  34. 34. VariablesTemplate:<p>Hello, {{name}}.</p>Hash:{ "name": "Bradley"}Output:<p>Hello, Bradley.</p>
  35. 35. Unescaped VariablesTemplate:<p>Hello, {{{name}}}.</p>Hash:{ "name": "<em>Bradley</em>"}Output:<p>Hello, <em>Bradley</em>.</p>
  36. 36. “Missed” VariablesTemplate:<p>Hello, {{name}}.</p>Hash:{ "role": "admin"}Output:<p>Hello, .</p>
  37. 37. Sections
  38. 38. False ValuesTemplate:<p>Shown.</p>{{#show}}<p>Not shown.</p>{{/show}}Hash:{ "show": false}Output:<p>Shown.</p>
  39. 39. Empty ListsTemplate:<p>Shown.</p>{{#show}}<p>Not shown.</p>{{/show}}Hash:{ "role": "admin"}Output:<p>Shown.</p>
  40. 40. Non-Empty ListsTemplate:{{#users}} <p>{{name}}</p>{{/users}}Hash:{ "users": [ { "name": "Bradley" }, { "name": "Jason" }, ]}Output: <p>Bradley</p> <p>Jason</p>
  41. 41. Non-False(and Not a List) ValuesTemplate:{{#user}} <p>Logged in as {{name}}</p>{{/user}}Hash:{ "user": { "name": "Bradley" }}Output: <p>Logged in as Bradley</p>
  42. 42. Inverted SectionsTemplate:{{^user}} <p>Not logged in</p>{{/user}}Hash:{ "user": false}Output: <p>Not logged in</p>
  43. 43. Evently
  44. 44. Evently is a jQuery plugin forwriting event-based applications
  45. 45. DOM Events<p id="foo"></p><script type="text/javascript">$("#foo").evently({ click: function() { $(this).text("Clicked"); }})</script>
  46. 46. Custom Events<p id="user"></p><script type="text/javascript">$("#user").evently({ login: function(e, user) { $(this).text(user); }})$("#user").trigger("login", "Bradley");</script>
  47. 47. Widgets$("#user").evently({ _init: { async: function(callback) { $.couch.session({ success: function(data) { callback(data); } }); }, mustache: "<span>{{#name}}Logged in as {{name}}{{/name}}{{^name}}Not loggedin{{/name}}</span>", data: function(data) { return data.userCtx; } }});
  48. 48. jQuery.data Syntactic Sugar// $$ inspired by @wycats: http://yehudakatz.com/2009/04/20/evented-programming-with-jquery/function $$(node) { var data = $(node).data("$$"); if (data) { return data; } else { data = {}; $(node).data("$$", data); return data; }};
  49. 49. $$ ExamplesThis:$("#my-node").data("key", "value");Becomes:$$("#my-node").key = "value";This:var value = $("#my-node").data("key");Becomes:var value = $$("#my-node").key;
  50. 50. Design Documents
  51. 51. CouchApps & Design DocumentsEach CouchApp is entirely self-contained in one design documentDesign documents live alongside other documents, but have a special namingconvention of _design/<name>A database can have multiple CouchApps (and/or design documents) andCouchDB server can host multiple databases
  52. 52. What’s in a design document?Can contain: • View de nitions • Map functions • Reduce functions • Show functions • List functions • Document update handlers • Document update validation functions • Rewrite de nitions • Any other elds you want, just like with any other document • Attachments, just like with any other documentID follows the form of:_design/<design_name>
  53. 53. Exploring Pages,a CouchApp Wiki
  54. 54. Page Comments, JavaScript
  55. 55. views/recent-comments/map.jsfunction(doc) { if (doc.type == "comment") { emit([doc.topic, doc.at], doc); }};
  56. 56. Wiring the Evently Widget <div id="comments"></div> …<script type="text/javascript" charset="utf-8"> var opts = {}; … $.couch.app(function(app) { … $$("#wiki").docid = {{docid}}; $$("#wiki").title = {{title_json}}; $("#comments").evently("comments", app); … }, opts);</script>
  57. 57. evently/comments/_init/query.jsfunction() { var docid = $$("#wiki").docid; return { view : "recent-comments", endkey : [docid, {}], startkey : [docid] };};
  58. 58. evently/comments/_init/data.jsfunction(view) { var docid = $$("#wiki").docid, linkup = $$("#wiki").app.require("vendor/couchapp/lib/linkup"); return { topic : docid, title : $$("#wiki").title, comments : view.rows.map(function(r) { var by = r.value.by || {}; return { gravatar_url : by.gravatar_url, by : by.nickname, at : r.key[1], comment : linkup.encode(r.value.comment) // move to view } }) }};
  59. 59. evently/comments/_init/mustache.html<h3>Comments about {{title}}</h3><ul> {{#comments}} <li> <div class="avatar"> {{#gravatar_url}}<img src="{{gravatar_url}}"/>{{/gravatar_url}} <div class="name"> {{by}} </div> </div> <p>{{{comment}}}</p> <em><span class="date">{{at}}</span></em> <div class="clear"></div> </li> {{/comments}}</ul>
  60. 60. evently/comments/_init/mustache.html (cont’d)<form> <label>Comment (w/ <a href="http://github.com/couchapp/couchapp"target="_new">linkup</a>*)</label> <br/> <!-- <input type="text" size="60" name="comment"> --> <textarea name="comment" rows="4" cols="60"></textarea> <input type="hidden" name="topic" value="{{topic}}"> <p><input type="submit" value="Save Comment"></p></form>
  61. 61. evently/comments/_init/after.jsfunction() { $(".date").prettyDate();};
  62. 62. evently/comments/_init/selectors/form/submit.jsfunction() { var form = $(this), app = $$(form).app, f = form.serializeObject(); f.type = "comment"; f.at = new Date(); f.by = $$("#pro le").pro le; app.db.saveDoc(f, { success : function() { $("#comments").trigger("_init"); } }) return false;};
  63. 63. All Comments, HTML
  64. 64. rewrites.json[ … { "from" : "/pages/comments", "to" : "_list/comments/all-comments", "query" : { "descending" : true, "limit" : 20 } }, …]
  65. 65. CouchDB supports virtual hosts via the“vhosts” server con guration section.This lets you route to a rewrite handlerbased on the Host HTTP header.{ "example.com": "/db/_design/tutorial/_rewrite", "www.example.com": "/db/_design/tutorial/_rewrite"}
  66. 66. views/all-comments/map.jsfunction(doc) { if (doc.type == "comment" && doc.comment) { emit(doc.at, doc); }};
  67. 67. lists/comments.jsfunction() { var row, ddoc = this, mustache = require("vendor/couchapp/lib/mustache"), markdown = require("vendor/couchapp/lib/markdown"), data = { title : "All Comments", site_title : this.couchapp.name, path : "/pages/comments", comments : [] }; provides("html", function() { while (row = getRow()) { log(row); data.comments.push(row.value) } send(mustache.to_html(ddoc.templates.comments, data, ddoc.templates.partials)); });};
  68. 68. templates/comments.html{{>header}}<ul> {{#comments}} <li> <span class="date">{{at}}</span>: {{#by}}{{name}} {{/by}} {{^by}}Unknown {{/by}} (<a href="../page/{{topic}}">{{topic}}</a>) <span class="idontknow">{{comment}}</span> </li> {{/comments}}</ul> </body> …</html>
  69. 69. templates/partials/header.html<!DOCTYPE html><html> <head> <title>{{title}} - {{site_title}}</title> <link rel="stylesheet" href="../style/base-min.css" type="text/css"> <link rel="stylesheet" href="../style/main.css" type="text/css"> </head> <body> <div id="header"> <div id="account"></div> <h1><a href="../page/index">{{site_title}}</a>: <a href="{{path}}">{{title}}</a></h1> </div> <div id="pro le"></div>
  70. 70. Securityvalidate_doc_update.js
  71. 71. Function De nitionfunction (newDoc, oldDoc, userCtx, secObj) { var v = require("vendor/couchapp/lib/validate").init(newDoc, oldDoc, userCtx, secObj); …}
  72. 72. Let Admins Do Anythingif (v.isAdmin()) { return true; // admin can do anything}
  73. 73. Require Login toMake Changesif (!userCtx.name) { // this could be con gurable based on secObj v.unauthorized("please login to make changes");}
  74. 74. Only Let Admins Delete// only admin may deleteif (newDoc._deleted) { v.unauthorized("only admin may delete docs");}
  75. 75. A special _security database propertylets you de ned database admins anddatabase readers
  76. 76. CouchApp Tool (Python)
  77. 77. CouchApps can be built withoutCouchApp tooling—but the toolingmakes things much easier.
  78. 78. Installing CouchApp$ sudo easy_install pip$ sudo pip install couchapp
  79. 79. CouchApp Tooling Basics
  80. 80. Generate a CouchApp$ couchapp generate tutorial2011-06-15 16:25:18 [INFO] /tutorial generated.$ cd tutorial
  81. 81. Generated Files$ tree -FL 1. README.md _attachments/ _id couchapp.json evently/ language lists/ shows/ updates/ vendor/
  82. 82. README.mdGenerated by CouchAppCouchApps are web applications which can be served directly from CouchDB. Thisgives them the nice property of replicating just like any other data stored in CouchDB.They are also simple to write as they can use the built-in jQuery libraries and pluginsthat ship with CouchDB.More info about CouchApps here.Deploying this appAssuming you just cloned this app from git, and you have changed into the app directoryin your terminal, you want to push it to your CouchDB with the CouchApp commandline tool…
  83. 83. _attachments$ tree -F _attachments_attachments index.html style/ main.css1 directory, 2 files
  84. 84. _id$ cat _id_design/tutorial
  85. 85. couchapp.json$ cat couchapp.json{ "name": "Name of your CouchApp", "description": "CouchApp"}
  86. 86. evently$ tree -F eventlyevently items/    _changes/    data.js    mustache.html    query.json profile/ profileReady/ mustache.html selectors/ form/
  87. 87. language$ cat languagejavascript
  88. 88. lists$ tree -F listslists0 directories, 0 files
  89. 89. shows$ tree -F showsshows0 directories, 0 files
  90. 90. updates$ tree -F updatesupdates0 directories, 0 files
  91. 91. vendor$ tree -FL 2 vendorvendor couchapp/ _attachments/ evently/ lib/ metadata.json4 directories, 1 file
  92. 92. views$ tree -F viewsviews recent-items/ map.js1 directory, 1 file
  93. 93. views/recent-items/map.js$ cat views/recent-items/map.jsfunction(doc) { if (doc.created_at) { emit(doc.created_at, doc); }};
  94. 94. Deploying a CouchApp
  95. 95. Deploy to Localhost$ couchapp push tutorial2011-06-16 11:16:05 [INFO] Visit your CouchApp here:http://127.0.0.1:5984/tutorial/_design/tutorial/index.html
  96. 96. My New CouchApp
  97. 97. Pathbinder
  98. 98. Pathbinder is a jQuery plugin for triggeringevents based on the path components of aURL hash.
  99. 99. Basic Usage<div id="pro le"></div><script type="text/javascript">$("#pro le").html(<p><a href="#/pro le">View Pro le</a></p>);$("#pro le").bind("pro le", function() { $(this).html("<p>Pro le goes here…</p>");});$("#pro le").pathbinder("pro le", "/pro le");</script>
  100. 100. Path Parameters<div id="pro le"></div><script type="text/javascript">$("#pro le").html(<p><a href="#/pro le/Bradley">View Bradleys Pro le</a></p>);$("#pro le").bind("pro le", function(e, params) { $(this).html("<p>Viewing " + params.id + "s pro le</p>");});$("#pro le").pathbinder("pro le", "/pro le/:id");</script>
  101. 101. Pathbinder,Evently & Mustache<div id="pro le"></div><script type="text/javascript">$("#pro le").evently({ _init: { path: "/", mustache: <p><a href="#/pro le/Bradley">View Pro le</a></p> }, viewPro le: { path: "/pro le/:id", mustache: <p>Viewing {{id}}s pro le; <a href="#/">Back</a></p>, data: function(e, params) { return params; } }});</script>
  102. 102. The path name can be stored in a path.txt lewithin an Evently widget in a CouchApp.
  103. 103. Alternative CouchApp Tools
  104. 104. node.couchapp.jsCommand-line tool, written for Node.jsSimpler folder structureNot compatible with the Python versionLinks: • https://github.com/mikeal/node.couchapp.js • http://japhr.blogspot.com/2010/04/quick-intro-to-nodecouchappjs.html • http://vimeo.com/26147136
  105. 105. Kanso FrameworkAlso built using Node.jshttp://kansojs.org/
  106. 106. soca“Sammy On Couch App” or “Sittin’ on a Couch App”Command-line tool written in RubyUses Sammy.jshttps://github.com/quirkey/soca
  107. 107. ReupholsterA simple way to get started developing CouchAppshttp://reupholster.iriscouch.com/reupholster/_design/app/index.html
  108. 108. See the CouchApp wiki for a list of CouchApps
  109. 109. Hosting is available through Iris Couchor Cloudant.
  110. 110. CouchDB & CouchApp ResourcesCouchApp Wiki Scaling CouchDBhttp://couchapp.org/ by Bradley Holt (O’Reilly) 063-6-920-01840-7CouchDB Wikihttp://wiki.apache.org/couchdb/ Beginning CouchDB by Joe Lennon (Apress)CouchDB: The De nitive Guide 978-1-430-27237-3by J. Chris Anderson, Jan Lehnardt, andNoah Slater (O’Reilly)978-0-596-15589-6Writing and Querying MapReduce Views inCouchDBby Bradley Holt (O’Reilly)978-1-449-30312-9
  111. 111. Questions?
  112. 112. Thank You Blog: http://bradley-holt.com @BradleyHolt (http://twitter.com/BradleyHolt) bradley.holt@foundline.comCopyright © 2011 Bradley Holt. All rights reserved.

×