In this tutorial, you build a micro-blogging application using multi-player Gaia
storage. Gaia is Blockstack's [decentralized high-performance storage
system](https://github.com/blockstack/gaia). The tutorial contains the following
topics:
* TOC
{:toc}
This tutorial does not teach you about authentication. That is covered in depth [in the hello-blockstack tutorial](hello-blockstack).
<!--TODO: authentication tutorial-->
<!--Strictly speaking not sure it is necessary here to send them out-->
{% include note.html content="This tutorial was written on macOS High Sierra 10.13.4. If you use a Windows or Linux system, you can still follow along. However, you will need to \"translate\" appropriately for your operating system. Additionally, this tutorial assumes you are accessing the Blockstack Browser web application via Chrome. The application you build will also work with a local installation and/or with browsers other than Chrome. " %}
## About this tutorial and the prerequisites you need
The storage application you build with this tutorial is a React.js application
that is completely decentralized and server-less. While not strictly required
to follow along, basic familiarity with React.js is helpful.
When complete, the app is capable of the following:
- authenticating users using Blockstack
- posting new statuses
- displaying statuses in the user profile
- looking up the profiles and statuses of other users
The basic identity and storage services are provided by `blockstack.js`. To test
the application, you need to have already [registered a Blockstack ID](ids-introduction).
The tutorial relies on the `npm` dependency manager. Before you begin, verify
you have installed `npm` using the `which` command.
```bash
$ which npm
/usr/local/bin/npm
```
If you don't find `npm` in your system, [install
it](https://www.npmjs.com/get-npm). Finally, if you get stuck at any point
while working on the tutorial, the completed [source code is available for
you](https://github.com/larrysalibra/publik) to check your work against.
## Use npm to install Yeoman and the Blockstack App Generator
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that
helps users rapidly start new projects and streamline the maintenance of
existing projects.
1. Install Yeoman.
```bash
npm install -g yo
```
2. Install the Blockstack application generator.
```bash
npm install -g generator-blockstack
```
<!-- Need to find out if user is required to have React installed before running Yeoman. Doesn't appear to be the case. -->
## Generate and launch the public application
In this section, you build an initial React.js application called Publik.
1. Create a the `publik` directory.
```bash
mkdir publik
```
2. Change into your new directory.
```bash
cd publik
```
3. Use Yeoman and the Blockstack application generator to create your initial `publik` application.
By default, authentication requests include the `store_write` scope which
enables storage. This is what allows you to store information to Gaia.
3. Save your changes.
4. Go back to your app at `http://localhost:8080/`.
5. Log out and sign in again.
The authentication request now prompts the user for permission to **Publish
data stored for the app**.
![](images/publish-data-perm.png)
## Understand Gaia storage methods
Once you authenticate a user with `store_write` and `publish_data`, you can
begin to manage data for your users. Blockstack JS provides two methods
`getFile()` and `putFile()` for interacting with Gaia storage. The storage
methods support all file types. This means you can store SQL, Markdown, JSON, or
even a custom format.
You can create a meaningful and complex data layer using these two methods.
Before creating an application, consider fundamental data architecture and make
some decisions about how you’re modeling data. For example, consider building a
simple grocery list app. A user should be able to create, read, update, and
delete grocery lists.
A single file collection stores items as an array nested inside each grocery
list:
```js
// grocerylists.json
{
"3255": {
"items": [
"1 Head of Lettuce",
"Haralson apples"
]
},
// ...more lists with items
}
```
This is conceptually the simplest way to manage grocery lists. When you read a
`/grocerylists.json` file with `getFile()`, you get back one or more grocery
lists and their items. When you write a single list, the `putFile()` method
overwrites the entire list. So, a write operation for a new or updated grocery
list must submit all existings lists as well.
Further, because this runs on the client where anything can go wrong. If the
client-side code encounters a parsing error with a user-input value and you
could overwrite the entire file with:
`line 6: Parsing Error: Unexpected token.`
Further, a single file makes pagination impossible and if your app stores a
single file for all list you have less control over file permissions. To avoid
these issues, you can create an index file that stores an array of IDs. These
IDs point to a name of another file in a `grocerylists` folder.
![](images/multiple-lists.png)
This design allows you to get only the files you need and avoid accidentally
overwriting all lists. Further, you’re only updating the index file when you add
or remove a grocery list; updating a list has no impact.
## Add support for user status submission and lookup
In this step, you add three `blockstack.js` methods that support posting of "statuses". These are the `putFile()`, `getFile()`, and `lookupProfile()` methods.
1. Open the `src/components/Profile.jsx` file.
2. Expand the `import from blockstack` statement with data methods.
The `Person` object holds a Blockstack profile. Add `putFile`, `getFile`,
and `lookupProfile` after `Person`.
When you are done, the import statement should look like the following:
```javascript
import {
isSignInPending,
loadUserData,
Person,
getFile,
putFile,
lookupProfile
} from 'blockstack';
```
3. Replace the `constructor()` initial state so that it holds the key properties required by the app.
This code constructs a Blockstack `Person` object to hold the profile. Your constructor should look like this:
```javascript
constructor(props) {
super(props);
this.state = {
person: {
name() {
return 'Anonymous';
},
avatarUrl() {
return avatarFallbackImage;
},
},
username: "",
newStatus: "",
statuses: [],
statusIndex: 0,
isLoading: false
};
}
```
4. Locate the `render()` method.
5. Modify the `render()` method to add a text input and submit button to the application.
The following code echos the `person.name` and `person.avatarURL`
You use `isLocal()` to check if the user is viewing the local user profile or another user's profile. If it's the local user profile, the app runs the `getFile()` function you added in an earlier step. Otherwise, the app looks up the profile belonging to the `username` using the `lookupProfile()` method.
6. Modify the `fetchData()` method like so:
```javascript
fetchData() {
this.setState({ isLoading: true })
if (this.isLocal()) {
const options = { decrypt: false }
getFile('statuses.json', options)
.then((file) => {
var statuses = JSON.parse(file || '[]')
this.setState({
person: new Person(loadUserData().profile),
username: loadUserData().username,
statusIndex: statuses.length,
statuses: statuses,
})
})
.finally(() => {
this.setState({ isLoading: false })
})
} else {
const username = this.props.match.params.username
lookupProfile(username)
.then((profile) => {
this.setState({
person: new Person(profile),
username: username
})
})
.catch((error) => {
console.log('could not resolve profile')
})
}
}
```
**NOTE**: For `https` deployments, the default Blockstack Core API endpoint for name
lookups should be changed to point to a core API served over `https`.
Otherwise, name lookups fail due to browsers blocking mixed content.
Refer to the [Blockstack.js
documentation](http://blockstack.github.io/blockstack.js/#getfile) for
details.
7. Add the following block to `fetchData()` right after the call to `lookupProfile(username)... catch((error)=>{..}` block:
Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile.
8. Replace the `render()` method with the following:
### This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition.
### Put it all together
1. Stop the running application by sending a CTL-C.
2. Restart the application so that the disabling of the `.` (dot) rule takes effect.
```bash
npm start
```
3. Point your browser to `http://localhost:8080/your_blockstack.id` to see the final application.
## Wrapping up
Congratulations, you are all done! We hope you've enjoyed learning a bit more