Make a Solid app on your lunch break

Submitted by super on Wed, 09/12/2018 - 19:25

This is a quick tutorial for new Solid developers. It is so quick that you can do this on your lunch break. In a couple of simple steps, we will build a profile viewer.

Learning objectives:

  • building a basic app with Solid
  • logging in and out
  • reading data from a Solid pod

Prerequisites:

  • basic HTML and CSS knowledge
  • intermediate JavaScript skills

Required tools:

  • your favorite text editor
  • a Web server to run your app locally (for example `npm install -g local-web-server`)

Step 0: Get yourself a Solid pod

In order to read and write data, you will need your own Solid POD and identity. Get a Solid POD if you haven't done so already.

Step 1: Set up a basic HTML page

Create an empty HTML document that will hold your application:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Profile Viewer</title>
</head>
<body>
  <h1>Profile viewer</h1>
</body>
</html>

Fire up your local Web server (for example `ws`) and view your page in the browser. Nice! 

Click here for the code for this step.

Step 2: Add jQuery

This is just a lunch break app, so we will simply use jQuery instead of one of the more advanced frameworks. This will allow us to focus on Solid itself. We're rapidly building out support for frameworks like Angular, React, and Vue too!

Click here for the code for this step.

Step 3: Add login status UI elements

Add two <p> elements to the HTML page: one that says the user is logged in, one that says they are not:

<p id="login">
  You are not logged in.
</p>
<p id="logout">
  You are logged in as <span id="user"></span>.
</p>

Use jQuery to hide the “logged in” message, since we do not have any actual authentication code yet.

Click here for the code for this step.

Step 4: Add the Solid auth client

The solid-auth-client library helps us with authenticating the user and securely fetching files from their pod. You need the following components:

Place the library before the main script, as we will be using its functionality there.

Click here for the code for this step.

Step 5: Add a login button

Add a login button to the page, and set up an event handler such that clicking that button triggers a Solid login window. This happens through the popupLogin function provided by window.solid.auth that is initialized by solid-auth-client. We pass the location of our popup window as a parameter.

const popupUri = 'popup.html';
$('#login button').click(() => solid.auth.popupLogin({ popupUri }));

We now want the interface to update when the user is logged in. solid.auth provides the trackSession function which will invoke your callback any time the user’s login status changes. It will pass an object with a webId property when the user is logged in, or null when they are not.

solid.auth.trackSession(session => {
  const loggedIn = !!session;
  $('#login').toggle(!loggedIn);
  $('#logout').toggle(loggedIn);
  $('#user').text(session && session.webId);
});

Test your application by logging in with your pod. (Remember to view the application through a local Web server, not the filesystem.)

Click here for the code for this step.

Step 6: Add a logout button

In a similar way, provide the user with logout functionality:

$('#logout button').click(() => solid.auth.logout());

Test your application by logging in and out.

Click here for the code for this step.

Step 7: Add an input element for the profile’s WebID

This profile viewer will be able to show data from different people’s profiles. Add an input element so people can select that profile:

<p>
  <label for="profile">Profile:</label>
  <input id="profile">
</p>

To simplify testing, you can make the profile default to the logged in user:

solid.auth.trackSession(session => {
  // …
  if (session) {
    $('#user').text(session.webId);
    if (!$('#profile').val())
      $('#profile').val(session.webId);
  }
});

Click here for the code for this step.

Step 8: Add RDFlib.js

RDFlib.js is a JavaScript library that allows us to interact with Linked Data stored in Solid pods. You can obtain it from https://linkeddata.github.io/rdflib.js/dist/rdflib.min.js.

Be sure to place this script tag after the one for solid-auth-client, such that RDFlib can use the authenticated fetch functionality.

Click here for the code for this step.

Step 9: Show the user’s name

solid-auth-client allows us to perform authenticated retrieval of data from pods, and RDFlib lets us parse and process that data. We will now fetch the user’s profile document, and look for a triple that indicates the name of the person. This is possible with the following code:

const FOAF = $rdf.Namespace('http://xmlns.com/foaf/0.1/');
$('#view').click(async function loadProfile() {
  // Set up a local data store and associated data fetcher
  const store = $rdf.graph();
  const fetcher = new $rdf.Fetcher(store);

  // Load the person's data into the store
  const person = $('#profile').val();
  await fetcher.load(person);

  // Display their details
  const fullName = store.any($rdf.sym(person), FOAF('name'));
  $('#fullName').text(fullName && fullName.value);
});

Click here for the code for this step.

Step 10: Show the user’s friends

Now that we can display a single person, we can also fetch their friends and show them:

$('#view').click(async () => {
  // …
  const person = $('#profile').val();
  // …
  const friends = store.each($rdf.sym(person), FOAF('knows'));
  $('#friends').empty();
  friends.forEach(async (friend) => {
    await fetcher.load(friend);
    const fullName = store.any(friend, FOAF('name'));
    $('#friends').append($('<li>')
       .text(fullName && fullName.value || friend.value));
  });
});

Note that the name of those friends is loaded from _their_ pods, not yours! In case your example account does not have friends yet, you can test with this profile in the viewer input box: https://ruben.verborgh.org/profile/#me.

Click here for the code for this step.

Step 11: Make the friends clickable

With a simple step, we can make the profile viewer more interactive. Instead of just listing the friends, we turn them into links. When a link is clicked, that person is loaded into the profile viewer:

$('#view').click(async function loadProfile() {
  // …
  const person = $('#profile').val();
  // …
  const friends = store.each($rdf.sym(person), FOAF('knows'));
  $('#friends').empty();
  friends.forEach(async (friend) => {
	await fetcher.load(friend);
	const fullName = store.any(friend, FOAF('name'));
	$('#friends').append(
  	$('<li>').append(
    	$('<a>').text(fullName && fullName.value || friend.value)
            	.click(() => $('#profile').val(friend.value))
            	.click(loadProfile)));
  });
});

This simple change shows how, thanks to the power of Linked Data, we can easily traverse data stored across different data pods.

Click here for the code for this step.