Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

6 No.!

Slide 7

Slide 7 text

7 Quite the opposite, in fact.!

Slide 8

Slide 8 text

8 Ubiquitous connectivity is driving the demand for 
 offline capabilities.!

Slide 9

Slide 9 text

9 How?!

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

11 Offline-first is the only way 
 to achieve a true, 100% 
 always-on user experience.* ! *assuming the device is reliable!

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Introducing PouchDB

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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
 }
 }

Slide 20

Slide 20 text

PouchDB Framework Adapters 17

Slide 21

Slide 21 text

Hybrid or Responsive Mobile Web Apps

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Apache CouchDB

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

PouchDB and CouchDB Replication 24

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Apache Cordova, PhoneGap and Ionic

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

HTML5 Offline Application Cache

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Cache Manifest File 32 
 …
 
 CACHE MANIFEST
 # v1 - 2015-01-08
 index.html
 logo.png
 app.css
 app.js

Slide 36

Slide 36 text

PouchDB Code Examples

Slide 37

Slide 37 text

Installing PouchDB 34 
 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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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"

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

Bradley Holt Cloudant Developer Advocate bradley.holt@us.ibm.com @BradleyHolt github.com/bradley-holt