Offline First Apps with PouchDB and CouchDB

A presentation at Front End North in January 2018 in Sheffield, UK by Lorna Jane Mitchell

Slide 1

Slide 1

OfflineFirst Apps With PouchDB and CouchDB Lorna Mitchell, IBM

Slide 2

Slide 2

OfflineFirst Apps Offline is not an error condition “Offline capability is a key characteristic of modern Progressive Web Applications” - http://offlinefirst.org @lornajane

Slide 3

Slide 3

Being Offline Could include: • being in a place without internet infrastructure • being without a data package on your phone • being on the tube or a plane @lornajane

Slide 4

Slide 4

Being Offline Could include: • being in a place without internet infrastructure • being without a data package on your phone • being on the tube or a plane • getting the train from Huddersfield to … really anywhere @lornajane

Slide 5

Slide 5

OfflineFirst OfflineFirst means failing gracefully You’ll also hear PWA which is a Progressive Web App, covering more than just network failures @lornajane

Slide 6

Slide 6

Achieving OfflineFirst: Code Service Worker caches on first page load @lornajane

Slide 7

Slide 7

Achieving OfflineFirst: Data Client-side app and storage, background sync @lornajane

Slide 8

Slide 8

PouchDB and CouchDB • https://pouchdb.com/ • A database that your client-side javascript can use • Can also sync to CouchDB (-compatible) databases • https://couchdb.apache.org • A NoSQL document database • Best replication on the planet (probably) • HTTP API = good support in all languages @lornajane

Slide 9

Slide 9

Example App: Shopping List • Client-side JavaScript with PouchDB • Works locally • If connected, syncs to Cloudant/CouchDB • Code here: https://github.com/lornajane/robust-shopping-list @lornajane

Slide 10

Slide 10

PouchDB in Action In index.html: <script src=”/js/pouchdb-6.1.2.min.js”></script> <script src=”/js/shopping.js”></script> shopping.js is where my client-side JavaScript lives @lornajane

Slide 11

Slide 11

PouchDB in Action 1 var db = new PouchDB(‘shopping’); 2 var remoteDB = new PouchDB(‘http://localhost:5984/shopping’); 3 window.onload = function() { 4 db.sync(remoteDB, { live: true, retry: true } 5 ).on(‘change’, function (change) { 6 return getItemList().then(function (contents) { 7 document.getElementById(‘itemList’).innerHTML = con 8 }) 9 }).on(‘active’, function (info) { 10 return getItemList().then(function (contents) { 11 document.getElementById(‘itemList’).innerHTML = con 12 }); 13 }); @lornajane

Slide 12

Slide 12

NoSQL Document Database @lornajane

Slide 13

Slide 13

NoSQL Document Database @lornajane

Slide 14

Slide 14

NoSQL Document Database @lornajane

Slide 15

Slide 15

Document Databases Store collections of schemaless documents @lornajane

Slide 16

Slide 16

Document Databases Choose a document database if: • the records you store don’t have the same structure as one another • you need to change data structures without downtime • you like high availability @lornajane

Slide 17

Slide 17

Data Design for PouchDB • data structure: include nested data/array, omit empty fields • identifiers: pick a meaningful ID where appropriate • beware updating/appending data: these cause conflicts @lornajane

Slide 18

Slide 18

CouchDB Cluster Of Unreliable Commodity Hardware • HTTP API • JSON data format • Performant views use JavaScript and MapReduce • Ad-hoc queries with a JSON structure using Mango @lornajane

Slide 19

Slide 19

Curl and Not-Curl • love curl? (https://curl.haxx.se/) • try jq (https://stedolan.github.io/jq/) • hate curl? Try one of these • http-console https://github.com/cloudhead/http-console • Postman https://www.getpostman.com/ • for more, try this HTTP Tools post (and comments): http://lornajane.net/posts/2017/http-tools-roundup @lornajane

Slide 20

Slide 20

Fauxton Friendly web interface @lornajane

Slide 21

Slide 21

Fauxton @lornajane

Slide 22

Slide 22

Fauxton @lornajane

Slide 23

Slide 23

CouchDB: Lovely Doc DB I could stop here: • JSON format • HTTP interface and nice web UI • Scales well • Modern, performant document database @lornajane

Slide 24

Slide 24

Changes Feed A feed containing all database changes. GET /_changes @lornajane

Slide 25

Slide 25

Replication @lornajane

Slide 26

Slide 26

Replication • Replication can be in either direction - or both • Can be one-off, or continuous • Other CouchDB-compatible storage also exists • e.g. PouchDB, a JavaScript implementation @lornajane

Slide 27

Slide 27

Conflicts Change docs in both places, replicate again: 87bf-bluemix.cloudant.com:443/shopping> GET /hat?conflicts=true { _id: ‘123’, _rev: ‘4-ecbc38075f9a8535c123e523519613b9’, item: ‘cheese’, _conflicts: [ ‘3-0bb689d59034fb769d99dcf697ae2de7’ ] } CouchDB will always choose the same “winning” doc @lornajane

Slide 28

Slide 28

Conflicts Fetch the “losing” doc(s) with ?rev= parameter 87bf-bluemix.cloudant.com:443/shopping> GET /123?rev=3-0bb689d5903 { _id: 123, _rev: ‘3-0bb689d59034fb769d99dcf697ae2de7’, item: ‘cheddar cheese’ } CouchDB doesn’t store old revisions forever @lornajane

Slide 29

Slide 29

Mango @lornajane

Slide 30

Slide 30

Mango: CouchDB Queries Mango is a mongo-like query language, useful for ad-hoc querying It is a JSON structure containing: • Selector: the criteria to match records on • Fields: which fields to return • Sort: what order you’d like that in (use with Skip) • Limit: how many records (default = 25) @lornajane

Slide 31

Slide 31

Mango: Example Query Use a query like this with the _find endpoint { “selector”: { “Year”: {“$eq”: “2012”} }, “fields”: [“Quarter”, “Product line”], “limit”: 5 } @lornajane

Slide 32

Slide 32

Mango: Example Query $ curl -X POST -H Content-Type:application/json \ http://localhost:5984/products/_find —data @mango.json {“warning”:”no “docs”:[ {“Quarter”:”Q1 {“Quarter”:”Q1 {“Quarter”:”Q1 {“Quarter”:”Q1 {“Quarter”:”Q1 ]} matching index found, create an index to optimize q 2012”,”Product 2012”,”Product 2012”,”Product 2012”,”Product 2012”,”Product line”:”Mountaineering line”:”Mountaineering line”:”Mountaineering line”:”Mountaineering line”:”Mountaineering Equipment”}, Equipment”}, Equipment”}, Equipment”}, Equipment”} @lornajane

Slide 33

Slide 33

Mango: Indexes Describe the index in JSON, then use the _index endpoint { “index”: { “fields”: [“Year”] }, “name”: “Year” } @lornajane

Slide 34

Slide 34

Mango: Indexes $ curl -X POST -H Content-Type:application/json \ http://localhost:5984/products/_index —data @index.json { “result”: “created”, “id”: “_design/e9b54f2ac34b8823ccbe8aaf6f406d464f50f521”, “name”: “Year” } Check which indexes are used by putting _explain where the _find normally goes! @lornajane

Slide 35

Slide 35

Views @lornajane

Slide 36

Slide 36

Views • Written in Javascript • Use MapReduce • The map results are stored • Can be used either for filtering, or for aggregation @lornajane

Slide 37

Slide 37

MapReduce Primer: Map • Examine each document, “emit” 0+ keys/value pairs • Scales well because each document is independent • To filter a collection of documents, use map step only @lornajane

Slide 38

Slide 38

MapReduce Primer: Map @lornajane

Slide 39

Slide 39

MapReduce Primer: Map @lornajane

Slide 40

Slide 40

MapReduce Primer: Map @lornajane

Slide 41

Slide 41

MapReduce Primer: Map @lornajane

Slide 42

Slide 42

MapReduce Primer: Reduce @lornajane

Slide 43

Slide 43

MapReduce Primer: Reduce • “Reduce” values in batches with the same key • CouchDB has useful built in functions for most things • Use reduce step when you want aggregate data • (SQL equivalent: a query with GROUP BY) @lornajane

Slide 44

Slide 44

Views Example @lornajane

Slide 45

Slide 45

OfflineFirst Apps With PouchDB and CouchDB @lornajane

Slide 46

Slide 46

Example Apps Ready-made shopping list examples are available: • VanillaJS and PouchDB (a more detailed example) • Polymer and PouchDB • React and PouchDB • Vue.js and PouchDB • React Native and PouchDB https://github.com/ibm-watson-data-lab/shopping-list @lornajane

Slide 47

Slide 47

Resources • https://lornajane.net • https://github.com/lornajane/robust-shopping-list • https://github.com/ibm-watson-data-lab/shopping-list • https://offlinefirst.org • http://hood.ie/ @lornajane