Today we’ll take a look at using Pusher in a Service Worker.
A Service Worker is a script that sits between your browser and the network, allowing you to respond to any requests made from your frontend. This can be used to serve content to users when they don’t have an active internet connection.
We’ll build an example app which will show a list of recent tweets mentioning the term ”javascript”. In browsers that support it, we’ll use Service Workers to make this page work offline.
tl,dr: Check the live example, it’s a list of tweets about javascript works offline.
Step one – build it all
Service Workers act as an enhancement to an existing site, so you don’t have change the way that you build your frontend (although, you do have to serve your final site over https).So, we’re going to start by ignoring our service worker and creating the site. To do this, we’ll make a web server that provides two urls:
GET /json
– pull the most recent tweets from a database, and serve it as jsonGET /
– give us some stuff to fetch the json and render it nicely
app.get('/json', (req, res, next) => redis .lrange( 'tweets', 0, -1 ) .then( result => result.map( JSON.parse ) ) .then( tweets => res.send( tweets ) ) .catch( next ))// expose / (index.html) from file systemapp.use(express.static('public'))
(note, the database is populated by a second script)Now we can deploy that online somewhere and we’re all done! We’ve got our page that displays the recent tweets.
Taking our app offline
Our next step is to make our application available offline. We can do this by writing a Service Worker script (this will be a new script that register from our main page).We want to request our two urls and store them in a cache, we can do this by calling
cache.addAll
when the service worker first starts up (firing the install
event).Once installed, we can respond to any requests from the page by hooking into a
fetch
event.self.addEventListener('install', (event) => event.waitUntil( caches.open('v1').then(cache => cache.addAll([ '/json', '/' ]) ) ))self.addEventListener('fetch', (event) => event.respondWith( caches.match(event.request) ))
Lastly, we need to register the service worker in our frontend:navigator.serviceWorker.register('/sw.js')
And now we’re all cool. Now we’re able to see our tweets even if we’re offline!Keeping things up to date
We have a problem. As our/json
endpoint is updated, the frontend will still be see the cached version without any new tweets.To handle this, we can change our caching policy to include a network request. (sw-toolbox is a great way to set up caching policies.)
Though, using the new pusher-js, we have another option, we can open a connection from our service worker and update the
/json
cache as soon as new data comes in.const add = (tweet) => caches.open(NAME) .then(cache => cache.match('/json') .then(resp => resp.json()) .then(tweets => cache.put('/json', new Response( JSON.stringify([tweet].concat(tweets)), {headers: { 'Content-Type': 'application/json' }} )) ) )pusher.subscribe('tweets') .bind('tweet', add)
This means that our cached version will be updated, without actually making any requests to the web server – which is kinda nuts when you think about it.We can deploy this, and now our
/json
endpoint will be updated with new content as it comes in.(still the same, but now it actually works.)
Note: there are other ways of getting data into a service worker – check out background sync and push notifications. The way we’re using Pusher here is slightly different from both.
Notifying users of new data
If we’re pushing things out to the user, we might as well notify the user of there being more content. A common way of doing this is to show an “X more things” label at the top of the feed.We can subscribe to messages in our page by adding an event listener to
navigator.serviceWorker
:navigator.serviceWorker.addEventListener('message',function(event) { console.log(event.data)})
And from the service worker, we can publish a message to all clients that are using this service worker:self.clients.matchAll() .then( clients => clients.forEach( client => client.postMessage(message) ) )
And we’re done.Something interesting about this is that although our page is seeing realtime data – the only connection to pusher is within the Service Worker, meaning:
Way cool. Technology
No comments:
Post a Comment