Skip to content

Instantly share code, notes, and snippets.

@marten-de-vries
Last active September 10, 2020 14:55
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save marten-de-vries/3bfaf635f9efdbcf5102 to your computer and use it in GitHub Desktop.
Save marten-de-vries/3bfaf635f9efdbcf5102 to your computer and use it in GitHub Desktop.
Running CouchApps on PouchDB in the browser using service workers - without any modifications!

Running CouchApps on PouchDB in the browser using service workers

What is this?

A description of how to run an existing CouchApp on PouchDB in the browser using service workers - without any modifications to existing code being necessary! The best thing is that if service workers aren't available, the CouchApp will still run as normal: that is, online.

Demo

https://brassbandwirdum.nl/

Only tested in Chromium currently, browser support for service workers is still rare. To learn more about this particular CouchApp (it's a simple website), see its design document in Futon.

Required PouchDB changes

pouchdb/pouchdb#3885

Required file changes

First, rewrites.json. Add a few endpoints so PouchDB can replicate from the db:

	{
		"from": "resources",
		"to": "fake-root.json"
	},
	{
		"from": "resources/db",
		"to": "../.."
	},
	{
		"from": "resources/db/*",
		"to": "../../*"
	},

_attachments/fake-root.json is just:

{"couchdb":"Welcome","uuid":"8e3e6d962d1833a6df7aca53aadfa857","version":"1.5.0","vendor":{"name":"Ubuntu","version":"14.04"}}

It shouldn't be necessary, but it is to trick PouchDB into replicating. Probably a (window.fetch()-only) PouchDB bug, but it needs more research into it to be sure.

Then, the nice stuff. Including the service worker in the main template (templates/page.html) is simple:

			if ('serviceWorker' in navigator) {
				navigator.serviceWorker.register('{{ &base }}/workerbundle.js');
			}

(&base is handled by the templating system used in this CouchApp)

The referred attachments/workerbundle.js is a browserified version of the file worker.js (see the next file in this Gist). While worker.js isn't that big, part of it could be abstracted away into a library of its own, making this process easier.

That's all!

'use strict';
/* global self, Response, fetch, Blob */
var PouchDB = require('pouchdb');
PouchDB.plugin(require("pouchdb-list"));
PouchDB.plugin(require("pouchdb-rewrite"));
PouchDB.plugin(require("pouchdb-show"));
PouchDB.plugin(require("pouchdb-validation"));
require("pouchdb-vhost")(PouchDB);
var liburl = require('url');
self.addEventListener('install', function (event) {
event.waitUntil(replicate().catch(function (err) {
// ignore errors, the user is probably just offline.
console.error(err.stack || err);
}));
});
function replicate(opts) {
var remote = new PouchDB('https://brassbandwirdum.nl/resources/db');
var db = new PouchDB('brassbandwirdum');
return remote.replicate.to(db, opts);
}
var replicating = null;
self.addEventListener('fetch', function (event) {
if (!replicating) {
replicating = replicate({live: true, retry: true});
}
var url = event.request.url
// TODO: remove once https://github.com/pouchdb/pouchdb/issues/3883 is
// fixed.
.replace('start_key', 'startkey')
.replace('end_key', 'endkey');
var urlInfo = liburl.parse(url, true);
if (urlInfo.host !== 'brassbandwirdum.nl') {
// fetch normally
return event.respondWith(fetch(event.request));
}
event.respondWith(PouchDB.virtualHost({
raw_path: urlInfo.path,
headers: {host: 'brassbandwirdum.nl'}
}, {
'brassbandwirdum.nl': '/brassbandwirdum/_design/brassbandwirdum/_rewrite/'
}, {
withValidation: true
}).then(function (resp) {
if (resp instanceof Blob) {
return {
code: 200,
body: resp,
headers: {"Content-Type": resp.type}
};
}
if (typeof resp.body === 'undefined') {
return {
code: 200,
body: JSON.stringify(resp),
headers: {"Content-Type": "application/json"}
};
}
return resp;
}).catch(function (err) {
return {
code: err.status || 500,
body: JSON.stringify({
error: err.name,
reason: err.message
})
};
}).then(function (resp) {
return new Response(resp.body, {
status: resp.code,
headers: resp.headers
});
}));
});
@marten-de-vries
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment