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

Offline-First Apps with PouchDB at ConFoo

Bradley Holt
February 25, 2016

Offline-First Apps with PouchDB at ConFoo

Web and mobile apps shouldn't stop working when there's no network connection. Based on Apache CouchDB, PouchDB is an open source syncing JavaScript database that runs within a web browser. Offline-first apps built using PouchDB can provide a better, faster user experience—both offline and online. Learn how to use the HTML5 Offline Application Cache, PouchDB, and CouchDB to build offline-enabled web and mobile apps.

Bradley Holt

February 25, 2016
Tweet

More Decks by Bradley Holt

Other Decks in Programming

Transcript

  1. ConFoo 2016
    Bradley Holt, Developer Advocate
    Thursday, February 25, 2016
    Offline-First Apps with PouchDB
    @BradleyHolt

    View Slide

  2. Why offline first?

    View Slide

  3. View Slide

  4. View Slide

  5. Mobile First
    Design for the smallest device first and then apply progressive enhancement techniques to
    take advantage of larger screen sizes

    View Slide

  6. Offline First
    Design for offline usage first and then apply progressive enhancement techniques to take
    advantage of network connectivity when available

    View Slide

  7. Ubiquitous Connectivity
    Why offline first in a world of ubiquitous connectivity?

    View Slide

  8. 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
    @BradleyHolt

    View Slide

  9. Mobile Backend
    What happens when your mobile backend service is unreachable?

    View Slide

  10. Benefits of Offline First

    View Slide

  11. Faster User Experience
    Better, faster user experience — both offline and online

    View Slide

  12. Works Offline
    Ability to disconnect and continue to work offline

    View Slide

  13. Battery and Bandwidth
    Limited access to power and communications infrastructure in disaster scenarios

    View Slide

  14. Offline-First Patterns and
    Anti-Patterns

    View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. Tools and Use Cases

    View Slide

  21. CouchDB Replication Protocol
    @BradleyHolt
    Cloudant Sync
    IBM Cloudant DBaaS
    CouchDB PouchDB
    CouchDB Replication Protocol

    View Slide

  22. @BradleyHolt

    View Slide

  23. PouchDB Code Examples

    View Slide

  24. View Slide

  25. IBM Cloudant
    §  Globally distributed data layer for web
    and mobile applications
    §  MongoDB-style queries
    §  Advanced geospatial capabilities
    §  Full text search indexing
    @BradleyHolt

    View Slide

  26. JSON Documents
    {

    _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

    }

    }
    @BradleyHolt

    View Slide

  27. Creating a Local PouchDB Database
    var db = new PouchDB("smart-meter");
    @BradleyHolt

    View Slide

  28. Creating a Remote PouchDB Database
    var remoteDb = new PouchDB("https://bradley-holt.cloudant.com/smart-meter");
    @BradleyHolt

    View Slide

  29. 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!
    @BradleyHolt

    View Slide

  30. Creating a New Document
    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);
    });
    @BradleyHolt

    View Slide

  31. Updating a Document
    db.put({
    _id: "2014-11-12T23:27:03.794Z",
    kilowatt_hours: 14
    }).then(function(response) {
    return db.get(response.id);
    }).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);
    });
    @BradleyHolt

    View Slide

  32. Deleting a Document
    db.put({
    _id: "2014-11-12T23:27:03.794Z",
    kilowatt_hours: 14
    }).then(function(response) {
    // Get the document
    return db.get(response.id);
    }).then(function(doc) {
    // Remove the document from the database
    return db.remove(doc);
    }).catch(function(error) {
    console.log(error);
    });
    @BradleyHolt

    View Slide

  33. Querying a Database with allDocs
    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);
    });
    @BradleyHolt

    View Slide

  34. allDocs Options
    §  include_docs
    – conflicts
    – attachments
    §  startkey
    §  endkey
    §  inclusive_end
    (true by default)
    §  limit
    §  skip
    §  descending
    §  key
    §  keys
    @BradleyHolt

    View Slide

  35. Querying a Database with Map/Reduce
    §  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
    @BradleyHolt

    View Slide

  36. Querying a Database with PouchDB Find
    §  Based on Cloudant Query, aka Mango
    §  MongoDB-style query language
    §  Define fields to index
    @BradleyHolt

    View Slide

  37. Replication Patterns

    View Slide

  38. One Database Per User
    @BradleyHolt
    Clemmie
    Danyel
    Shelba
    Manuel Francis
    Marissa
    Mitchel
    Georgianne
    Garnet
    Audrey
    Kalyn

    View Slide

  39. Write-Only Replication
    §  Data generated on the device
    §  Replicate this data to the cloud from
    multiple users and/or devices
    §  Example uses:
    –  User updates
    –  Sensor data
    @BradleyHolt

    View Slide

  40. Write-Only Replication
    var db = new PouchDB("smart-meter");
    var remoteDb = new PouchDB(
    "https://bradley-holt.cloudant.com/smart-meter"
    );
    @BradleyHolt

    View Slide

  41. Write-Only Replication
    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);
    });
    @BradleyHolt

    View Slide

  42. Write-Only Replication
    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);
    });
    @BradleyHolt

    View Slide

  43. Read-Only Replication
    §  Data generated in the cloud
    §  Replicate this data to multiple users
    and/or devices from
    the cloud
    §  Example uses:
    –  Weather
    –  Notifications
    @BradleyHolt

    View Slide

  44. Read-Only Replication
    remoteDb.replicate.to(db, {
    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);
    });
    @BradleyHolt

    View Slide

  45. Bidirectional Replication
    §  Data generated on both
    the device and in the cloud
    §  Replicate this data to/from
    the cloud and to/from multiple
    users and/or devices
    §  Useful when you need to
    share data between users
    and/or devices
    @BradleyHolt

    View Slide

  46. Bidirectional Replication
    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);
    });
    @BradleyHolt

    View Slide

  47. Live Replication
    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);
    });
    @BradleyHolt

    View Slide

  48. Filtered Replication
    §  Select (with a function) which
    documents to replicate
    §  Filter can be defined locally
    within PouchDB, or remotely
    on Cloudant
    @BradleyHolt

    View Slide

  49. Filtered Replication
    db.replicate.to(remoteDb, {
    filter: function(doc) {
    return doc._id >= "2014-11-13T00:00:00.000Z";
    }
    }).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);
    });
    @BradleyHolt

    View Slide

  50. View Slide

  51. Boilerplates & Tools
    §  Frontend Web Apps
    –  React Boilerplate with Service Workers

    §  Backend Web Apps
    –  PouchDB npm Package

    –  PouchDB Server npm Package

    §  Mobile Apps
    –  PouchDB for Ionic Framework

    –  "Hello world" Cordova app with PouchDB

    –  "Hello world" Cordova app with PouchDB, using the SQLite Plugin

    –  Cloudant FoodTracker (uses Cloudant Sync for iOS)

    §  Desktop Apps
    –  PouchDB for Electron (formerly Atom Shell)

    –  PouchDB for Chrome packaged apps

    –  "Hello world" Chrome app with PouchDB

    –  PouchDB for NW.js (aka Node-Webkit)

    §  Internet of Things (IoT) Apps
    –  Node-RED

    @BradleyHolt

    View Slide

  52. Cloudant FoodTracker

    View Slide

  53. View Slide

  54. Image Credits
    §  A mockup of the golden Apple iPhone 5S by Zach Vega,
    on Wikimedia Commons

    §  Joan Touzet (@wohali), ASF Member, CouchDB PMC Member

    §  Device landscape by Jeremy Keith, on Flickr

    §  Cloud Formation Over the Adirondacks by Bradley Holt,
    on Twitter

    §  Cell phone tower by Gary Lerude, on Flickr

    §  Pneumatic Central by Sleestak, on Flickr

    §  Colunas by Daniel Zanini H., on Flickr
    §  Speed DLR on Doklands by Umberto Rotundo, on Flickr

    §  Waterfall by Paulo Valdivieso, on Flickr
    §  Wildfire by U.S. Fish and Wildlife Service Southeast Region,
    on Flickr
    §  Arduino Uno by Pete Prodoehl, on Flickr

    §  Grunge Warning Sign - Do Not Read This Sign by
    Nicolas Raymond, on Flickr
    §  Mango with section on a white background by bangdoll, on Flickr

    @BradleyHolt

    View Slide

  55. Questions?
    @BradleyHolt

    View Slide