Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Offline-Enabled Apps with PouchDB at php[tek] 2015

Building Offline-Enabled Apps with PouchDB at php[tek] 2015

Mobile web apps shouldn't stop working when there's no network connection. Offline-enabled apps built using PouchDB can provide a better, faster user experience while potentially reducing battery and bandwidth usage. Based on the Apache CouchDB database, PouchDB is an open-source syncing JavaScript database that runs within a web browser. Learn how to use the HTML5 Offline Application Cache, PouchDB, CouchDB, and Cordova/PhoneGap to develop fully-featured and cross-platform native apps, responsive mobile web apps, or high-fidelity prototypes that work just as well offline as they do online.

Bradley Holt

May 20, 2015
Tweet

More Decks by Bradley Holt

Other Decks in Programming

Transcript

  1. Building Offline-Enabled Apps with PouchDB
    php[tek]
    Wednesday, May 20, 2015
    Bradley Holt, Cloudant Developer Advocate
    @BradleyHolt

    View Slide

  2. Image Credit: Joan Touzet (@wohali), ASF Member, CouchDB PMC Member
    2

    View Slide

  3. Image Credit: Device landscape by Jeremy Keith, on Flickr
    3
    Not just mobile first…!

    View Slide

  4. Image Credit: Lynn Camp Prong (Explored) by AllieKF, on Flickr
    4
    Offline First!
    Because being offline shouldn't be an error condition.!

    View Slide

  5. 5
    Doesn't ubiquitous
    connectivity make offline-
    enabled apps unnecessary?!

    View Slide

  6. 6
    No.!

    View Slide

  7. 7
    Quite the opposite, in fact.!

    View Slide

  8. 8
    Ubiquitous connectivity is
    driving the demand for 

    offline capabilities.!

    View Slide

  9. 9
    How?!

    View Slide

  10. The Eight Fallacies of Distributed Computing
    1.  The network is reliable
    2.  Latency is zero
    3.  Bandwidth is infinite
    4.  The network is secure
    5.  Topology doesn't change
    6.  There is one administrator
    7.  Transport cost is zero
    8.  The network is homogeneous
    10
    Text Credit: The Eight Fallacies of Distributed Computing by Peter Deutsch | Image Credit: Pneumatic Central by Sleestak, on Flickr

    View Slide

  11. 11
    Offline-first is the only way 

    to achieve a true, 100% 

    always-on user experience.*
    !
    *assuming the device is reliable!

    View Slide

  12. Benefits of Offline First
    12
    •  Better, faster user experience, both offline and online
    •  Allow your users to work offline or with limited connectivity
    •  Potentially saves battery life and bandwidth usage

    View Slide

  13. Offline Patterns & Anti-Patterns
    •  Don't return an error for no reason
    •  Do let users view cached/saved data
    •  Do synchronize data when connected
    13

    View Slide

  14. Offline Patterns & Anti-Patterns
    •  Don't return an error for no reason
    •  Do let users view cached/saved data
    •  Do synchronize data when connected
    13

    View Slide

  15. Offline Patterns & Anti-Patterns
    •  Don't return an error for no reason
    •  Do let users view cached/saved data
    •  Do synchronize data when connected
    13

    View Slide

  16. Offline Patterns & Anti-Patterns
    •  Don't return an error for no reason
    •  Do let users view cached/saved data
    •  Do synchronize data when connected
    13

    View Slide

  17. Introducing PouchDB

    View Slide

  18. PouchDB
    •  A database in your web browser
    •  Can synchronize with any
    database that implements the
    CouchDB Replication Protocol
    •  Makes create, read, update and
    delete operations extremely fast
    15

    View Slide

  19. JSON Documents
    16
    {

    _id: "6EF9D2B0-13D3-1378-8D30-39E3CE0B36C2",

    _rev: "1-0b457efcf82fb29492ef927ba5b6ee15",

    type: "Feature",

    geometry: {

    type: "Point",

    coordinates: [

    -71.1028,

    42.3691

    ]

    },

    properties: {

    session_id: "3486b13f-7b8a-8a96-dfbf-9b82800e367f",

    timestamp: 1422928591717

    }

    }

    View Slide

  20. PouchDB Framework Adapters
    17

    View Slide

  21. Hybrid or Responsive Mobile Web Apps

    View Slide

  22. Hybrid Mobile Web Apps
    •  Native mobile web apps built with
    HTML5, CSS and JavaScript
    •  Good for:
    •  Fully-featured, cross-platform native apps
    •  High-fidelity prototypes
    19

    View Slide

  23. Responsive Mobile Web Apps
    •  HTML5, CSS and JavaScript mobile
    web apps
    •  Responsive design
    •  Enhanced to enable offline usage
    20

    View Slide

  24. Apache CouchDB

    View Slide

  25. Apache CouchDB
    •  JSON document database
    •  HTTP API
    •  Master-master replication
    22

    View Slide

  26. IBM Cloudant
    •  Globally distributed data layer for
    web and mobile applications
    •  MongoDB-style queries
    •  Advanced geospatial capabilities
    •  Full text search indexing
    23

    View Slide

  27. PouchDB and CouchDB Replication
    24

    View Slide

  28. CouchDB Replication Protocol
    •  One-off, one way operation
    •  Peer-to-peer (masterless)
    •  Incremental
    •  Conflict detection
    25

    View Slide

  29. Apache Cordova, PhoneGap and Ionic

    View Slide

  30. Apache Cordova and PhoneGap
    27
    Image Credit: build.phonegap by Andrés Álvarez Iglesias, on Flickr

    View Slide

  31. Ionic
    •  Mobile-optimized HTML, CSS
    and JavaScript components
    •  Builds on Apache Cordova
    •  Utilizes AngularJS
    •  CLI installable via npm
    28

    View Slide

  32. Using Cordova and Ionic
    $ npm install -g cordova ionic"
    $ ionic start energy-monitor tabs"
    $ cd energy-monitor"
    $ ionic platform add ios"
    $ ionic build ios"
    $ ionic emulate ios"
    29
    Image Credit: Getting Started with Ionic

    View Slide

  33. HTML5 Offline Application Cache

    View Slide

  34. HTML5 Offline Application Cache
    •  Enables fully-functional offline
    web apps
    •  Stores files and assets for
    offline browsing
    •  Makes page loads very fast,
    even when online
    31

    View Slide

  35. Cache Manifest File
    32

    …


    CACHE MANIFEST

    # v1 - 2015-01-08

    index.html

    logo.png

    app.css

    app.js

    View Slide

  36. PouchDB Code Examples

    View Slide

  37. Installing PouchDB
    34
    "https://cdn.jsdelivr.net/pouchdb/3.5.0/pouchdb.min.js"
<br/>>

    CACHE MANIFEST

    # v2 - 2015-01-08

    index.html

    logo.png

    app.css

    app.js

    https://cdn.jsdelivr.net/pouchdb/3.5.0/pouchdb.min.js

    View Slide

  38. PouchDB Code Examples
    35
    •  Creating a local PouchDB database
    •  Creating a remote PouchDB database
    •  Creating a new document
    •  Updating a document
    •  Deleting a document
    •  Querying a database
    •  Replicating PouchDB
    •  Listening for database changes

    View Slide

  39. Creating a Local PouchDB Database
    36
    var db = new PouchDB("smart-meter");"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/01-create-local-database.js

    View Slide

  40. Creating a Remote PouchDB Database
    37
    var remoteDb = new PouchDB("https://bradley-holt.cloudant.com/smart-meter");"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/02-create-remote-database.js

    View Slide

  41. Cross-Origin Resource Sharing (CORS)
    •  Enable Cross-Origin Resource
    Sharing (CORS) on remote database
    •  Browsers place security restrictions
    on cross-site HTTP requests
    •  If you run into a problem, remember
    this warning!
    38
    Image Credit: Grunge Warning Sign - Do Not Read This Sign by Nicolas Raymond, on Flickr

    View Slide

  42. Creating a New Document
    39
    var db = new PouchDB("smart-meter");"
    db.post({"
    date: "2014-11-12T23:27:03.794Z","
    kilowatt_hours: 14"
    }).then(function() {"
    console.log("Document created");"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/03-create-document-post.js

    View Slide

  43. Creating a New Document, Specifying its ID
    40
    var db = new PouchDB("smart-meter");"
    db.put({"
    _id: "2014-11-12T23:27:03.794Z","
    kilowatt_hours: 14"
    }).then(function() {"
    console.log("Document created");"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/04-create-document-put.js

    View Slide

  44. Updating a Document
    41
    db.put({"
    _id: "2014-11-12T23:27:03.794Z","
    kilowatt_hours: 14"
    }).then(function() {"
    return db.get("2014-11-12T23:27:03.794Z");"
    }).then(function(doc) {"
    // Update the value for kilowatt hours"
    doc.kilowatt_hours = 15;"
    // Put the document back to the database"
    return db.put(doc);"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/05-update-document.js

    View Slide

  45. Deleting a Document
    42
    db.put({"
    _id: "2014-11-12T23:27:03.794Z","
    kilowatt_hours: 14"
    }).then(function() {"
    // Get the document"
    return db.get("2014-11-12T23:27:03.794Z");"
    }).then(function(doc) {"
    // Remove the document from the database"
    return db.remove(doc);"
    }).catch(function(error) {"
    console.log(error);"
    });"
    "
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/06-delete-document.js

    View Slide

  46. Querying a Database with allDocs"
    43
    db.bulkDocs(["
    {_id: "2014-11-12T23:27:03.794Z", kilowatt_hours: 14},"
    {_id: "2014-11-13T00:52:01.471Z", kilowatt_hours: 15},"
    {_id: "2014-11-13T01:39:28.911Z", kilowatt_hours: 16},"
    {_id: "2014-11-13T02:52:01.471Z", kilowatt_hours: 17}"
    ]).then(function(result) {"
    // Get all documents"
    return db.allDocs({include_docs: true});"
    }).then(function(response) {"
    console.log(response);"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/07-query-database-all-docs.js

    View Slide

  47. allDocs Options
    •  include_docs"
    •  conflicts"
    •  attachments"
    •  startkey"
    •  endkey"
    •  inclusive_end
    (true by default)
    •  limit"
    •  skip"
    •  descending"
    •  key"
    •  keys"
    44

    View Slide

  48. Querying a Database with Map/Reduce
    45
    •  Most queries can be done with allDocs (in PouchDB)
    •  Map functions transform documents into indexes
    •  Reduce functions aggregate results of Map functions
    •  _sum"
    •  _count"
    •  _stats"

    View Slide

  49. Querying a Database with PouchDB Find
    •  Based on Cloudant Query (Mango)
    •  MongoDB-style query language
    •  Define fields to index
    46
    Image Credit: Mango with section on a white background by bangdoll, on Flickr

    View Slide

  50. Replicating a PouchDB Database
    47
    var db = new PouchDB("smart-meter");"
    var remoteDb = new PouchDB("
    "https://bradley-holt.cloudant.com/smart-meter""
    );"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/08-replicate-database.js

    View Slide

  51. Replicating a PouchDB Database
    48
    db.bulkDocs(["
    {_id: "2014-11-12T23:27:03.794Z", kilowatt_hours: 14},"
    {_id: "2014-11-13T00:52:01.471Z", kilowatt_hours: 15},"
    {_id: "2014-11-13T01:39:28.911Z", kilowatt_hours: 16},"
    {_id: "2014-11-13T02:52:01.471Z", kilowatt_hours: 17}"
    ]).then(function(result) {"
    …"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/08-replicate-database.js

    View Slide

  52. Replicating a PouchDB Database
    49
    db.replicate.to(remoteDb, {"
    live: false,"
    retry: false"
    }).on("change", function(info) {"
    // Replication has written a new document"
    console.log(info);"
    }).on("complete", function(info) {"
    // Replication has complete or been cancelled"
    console.log(info);"
    }).on("error", function(error) {"
    // Replication has stopped due to an unrecoverable failure"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/08-replicate-database.js

    View Slide

  53. Bidirectionally Replicating a PouchDB Database
    50
    Promise.all(["
    db.bulkDocs(["
    {_id: "2014-11-12T23:27:03.794Z", kilowatt_hours: 14},"
    {_id: "2014-11-13T00:52:01.471Z", kilowatt_hours: 15}"
    ]),"
    remoteDb.bulkDocs(["
    {_id: "2014-11-11T22:35:01.433Z", kilowatt_hours: 11},"
    {_id: "2014-11-12T00:43:01.633Z", kilowatt_hours: 13}"
    ])"
    ]).then(function() {"
    …"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/09-replicate-database-bidirectional.js

    View Slide

  54. Bidirectionally Replicating a PouchDB Database
    51
    db.sync(remoteDb, {"
    live: false,"
    retry: false"
    }).on("change", function(info) {"
    // Replication has written a new document"
    console.log(info);"
    }).on("complete", function(info) {"
    // Replication has complete or been cancelled"
    console.log(info);"
    }).on("error", function(error) {"
    // Replication has stopped due to an unrecoverable failure"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/09-replicate-database-bidirectional.js

    View Slide

  55. Live Replication of a PouchDB Database
    52
    Promise.all(["
    db.bulkDocs(["
    {_id: "2014-11-12T23:27:03.794Z", kilowatt_hours: 14},"
    {_id: "2014-11-13T00:52:01.471Z", kilowatt_hours: 15}"
    ]),"
    remoteDb.bulkDocs(["
    {_id: "2014-11-11T22:35:01.433Z", kilowatt_hours: 11},"
    {_id: "2014-11-12T00:43:01.633Z", kilowatt_hours: 13}"
    ])"
    ]).then(function() {"
    …"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/10-replicate-database-live.js

    View Slide

  56. Live Replication of a PouchDB Database
    53
    var sync = db.sync(remoteDb, {"
    live: true,"
    retry: true"
    }).on("change", function(info) {"
    // Replication has written a new document"
    console.log(info);"
    }).on("complete", function(info) {"
    // Replication has complete or been cancelled"
    console.log(info);"
    }).on("error", function(error) {"
    // Replication has stopped due to an unrecoverable failure"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/10-replicate-database-live.js

    View Slide

  57. Listening for Database Changes
    54
    var changes = remoteDb.changes({"
    since: "now""
    }).on("change", function(change) {"
    // A document has changed"
    console.log(change);"
    }).on("complete", function(info) {"
    // changes() was canceled"
    console.log(info);"
    }).on("error", function(error) {"
    // changes() has errored"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/11-database-changes.js

    View Slide

  58. Listening for Database Changes
    55
    // Create several documents"
    remoteDb.bulkDocs(["
    {_id: "2014-11-11T22:35:01.433Z", kilowatt_hours: 11},"
    {_id: "2014-11-12T00:43:01.633Z", kilowatt_hours: 13},"
    {_id: "2014-11-12T02:42:52.284Z", kilowatt_hours: 14},"
    {_id: "2014-11-12T02:46:23.730Z", kilowatt_hours: 16}"
    }).catch(function(error) {"
    console.log(error);"
    });"
    https://github.com/bradley-holt/offline-first/blob/master/pouchdb/11-database-changes.js

    View Slide

  59. Location Tracker
    •  Stores data locally in PouchDB
    •  Front end built with AngularJS
    •  Authentication logic built with Node.js
    •  User interface built with Leaflet
    •  Replicates location data to Cloudant
    •  More info:
    https://cloudant.com/location-tracker/
    56

    View Slide

  60. Image Credits
    57
    •  Joan Touzet (@wohali), ASF Member, CouchDB PMC Member

    •  Device landscape by Jeremy Keith, on Flickr

    •  Lynn Camp Prong (Explored) by AllieKF, on Flickr

    •  Pneumatic Central by Sleestak, on Flickr

    •  build.phonegap by Andrés Álvarez Iglesias, on Flickr

    •  Getting Started with Ionic

    •  Mango with section on a white background by bangdoll, on Flickr

    •  Grunge Warning Sign - Do Not Read This Sign by Nicolas Raymond, on Flickr

    View Slide

  61. Bradley Holt
    Cloudant Developer Advocate
    [email protected]
    @BradleyHolt
    github.com/bradley-holt

    View Slide