JavaScript dapps: Using decentralized storage in Blockstack

Blockstack.js gives users full control of their application data in fewer than 10 lines of code

JavaScript dapps: Using decentralized storage in Blockstack
JustSuper / Getty

Last week we put together a basic component that allowed our users to log in with Blockstack and see their profile. Since then, I’ve made some significant changes to incorporate Redux, Redux-saga, and React Router. Most of these changes are merely structural, but one change does alter functionality. We’ll look at that change below.

I also spent some time laying out the app and adding basic snippet functionality. With the functional base of our application complete, we’re in a good position to start exploring more decentralized behavior. This week we’ll look into adding decentralized persistence using convenient methods provided to us by the Blockstack JavaScript library, Blockstack.js.

Before we get into adding persistence to our snippets application, let’s review my change to the log-in flow and introduce the basic snippet functionality I’ve added since last week.

Routing with React Router

I added React Router to help manage the application and adjusted some functionality to handle a new log-in from a separate route. Now, after we click the log-in button and we are redirected to the Blockstack Browser for authentication, Blockstack sends us back to a /callback endpoint. This type of hand-off to a dedicated endpoint is relatively common with OAuth workflows, so you may be familiar with it already.

By default, the Blockstack Browser will redirect the user back to the site they came from, so we’ll have to pass in a bit of additional configuration when we call redirectToSignIn. This should be as easy as passing a single argument to that method:

blockstack.redirectToSignIn('/callback');

However, there is a bug in the Blockstack.js library that requires us to add a bit of a workaround here. The workaround below uses the upcoming UserSession approach to managing Blockstack users in order to get around this compatibility issue.

  const appConfig = new blockstack.AppConfig(undefined, undefined, '/callback');
  const userSession = new blockstack.UserSession({ appConfig })
  userSession.redirectToSignIn();

Blockstack app with basic snippet functionality

Now when the page loads, we are immediately presented with an example application that lets you build and test web application snippets.

blockstack decentralized snippets IDG

An example app loads right off the bat, so we can jump in immediately and begin editing web snippets.

Users can change the HTML, CSS, and JavaScript in the example app to their heart’s content and see the results in the bottom panel as they go. However, when they refresh the browser, all their work goes away and they’re presented with the stock example. Let’s change that so if they’re signed in with Blockstack, they’ll be presented with an option to save their progress, which will be loaded up when they sign in.

Working with storage in Blockstack

With Blockstack.js, adding decentralized storage is as simple as it was to add decentralized authentication. The two methods we’ll use are getFile and putFile. Both methods are asynchronous and both take a filename as the first argument. The putFile method also takes a second argument, which is a string to save as the file. Thinking about persistence as files to write may be somewhat unfamiliar, since most front-end persistence involves API calls to back-end services. In our case, we can just save a JSON file that contains an object with HTML, CSS, and JavaScript values. That object will look something like this:

{
  "html": "<div class=\"message\">The rotat….",
  "css": ".message { \n  text-align: center;\n …",
  "js": "const colors = ['red', 'blue', 'orange', …",
}

I will be using Redux-saga to handle any asynchronous calls in the app, but for the sake of clarity I’ll show you the raw JavaScript version of decentralized persistence without using the Redux-saga library:

const filename = 'snippet.json';
// Save a file to decentralized storage
putFile(filename, JSON.stringify({ 'html': '', 'css': '', 'css': ''}))
  .then(() => console.log('Save Successful!'))
  .catch((e) => console.error('Save failed!', e));
// Later on, we'll get that same file
getFile(filename)
  .then((contents) => {
    let obj = JSON.parse(contents);
    console.log('Initially saved object', obj);
  })
  .catch((e) => console.error('Could not get the file', e));

It couldn’t be easier!

To save our web app snippets, all we need to do is give the user a save button that triggers a call to putFile with the current contents of the snippet inputs. Then, when we load up the page, we can check if a user is logged in and, if so, call getFile and populate the snippet inputs with the contents of that file.

There you have it: a fully functional web application with decentralized authentication and storage, all provided by Blockstack.js in a few lines of code. We’re not done yet, though. Next week we’ll look at how to manage multiple snippets. As always, you’ll find the code for today’s column on GitHub. Questions or comments? Let’s continue the conversation on Twitter: @freethejazz

Copyright © 2019 IDG Communications, Inc.