Skip to content

Tutorial walkthrough

Ruben Taelman edited this page Oct 21, 2019 · 14 revisions

This is a tutorial on how to build a simple Solid app using GraphQL-LD.

Concretely, this tutorial explains how to build a Solid profile viewer in React using the GraphQL-LD Solid React Components.

The final version of the profile viewer can be found on https://comunica.github.io/Tutorial-Solid-GraphQL-LD-Profile-Viewer/, and the full source code of this app is available at https://github.com/comunica/Tutorial-Solid-GraphQL-LD-Profile-Viewer.

This profile viewer is equivalent to the LDflex-based profile viewer, but this time built using GraphQL-LD.

Requirements

You will need the following to follow this tutorial:

  • A Solid account (follow this tutorial if you don't have one yet)
  • git
  • Node.js (version 10.0 or higher)
  • Any kind of editor that be used to edit JavaScript files.
  • Any modern Web browser

Steps

  1. Creating a React app
  2. Enabling Authentication
  3. Viewing Profile
  4. Linking Friends

1. Creating a React app

Initializing a React project is very easy. Assuming you have Node.js installed, you can execute the following command:

npx create-react-app profile-viewer-graphql-ld

This will create an npm project in the profile-viewer-graphql-ld directory with all the required dependencies, and it install them.

After that, you can enter the directory and start a live development Webserver to look at the newly initialized app:

cd profile-viewer-graphql-ld
npm start # or yarn start 

This should open a new tab in your browser for http://localhost:3000/. If you see a spinning React icon, then everything is working as intended.

2. Enabling Authentication

In this step, we are going to make it possible to log into the app using a Solid account.

For this, we can use some of the pre-existing React components for Solid, which are available in the @solid/react package. Let's add this package as a dependency by running:

npm install @solid/react

We are going to use the following authentication components:

  • LoggedOut: Renders HTML only if you are not logged in.
  • LoggedIn: Renders HTML only if you are logged in.
  • AuthButton: Shows a login/logout button.

Note that the AuthButton requires a separate login popup file to be added to your project, which you can add to your public/ folder under the name popup.html. The following UNIX command should do the trick:

curl https://solid.github.io/solid-auth-client/dist/popup.html > public/popup.html

Next, let's actually add those components into our app by modifying our App() function in App.js to the following:

import React from 'react';
import { LoggedIn, LoggedOut, AuthButton } from '@solid/react';
import './App.css';

function App() {
  return (
    <div>
      <h1>Profile viewer</h1>
      <p>
        <LoggedOut>You are not logged in.</LoggedOut>
        <LoggedIn>You are logged in.</LoggedIn>
        <AuthButton popup="popup.html"/>
      </p>
    </div>
  );
}

export default App;

After this, you should be able to log in and out within your app.

3. Viewing Profile

In this part, we will implement the logic for extracting parts of a Solid profile and displaying them using GraphQL-LD. We will first use a hardcoded profile, and after that, we will modify our app to display the profile of the currently logged in user.

3.1. Adding Dependencies

In this tutorial, we will use GraphQL-LD queries to extract data from profiles.

For this, we need to add two dependencies to our app:

This can be done by executing the following:

npm install solid-react-graphql-ld graphql-tag

3.2. Viewing a Public Profile

Next, we will add two React components that will allow us to render HTML based on a given GraphQL-LD query:

  • GraphQlLdProvider: Initializes a GraphQL-LD query engine based on a list of sources to query from. Optionally, you can configure a custom JSON-LD context for converting GraphQL fields to URIs, by default we use @solid/context.
  • Query: Executes a given query and renders an HTML component based on the results.

First, add the following imports to the top of your App.js file:

import { GraphQlLdProvider, Query } from 'solid-react-graphql-ld';
import gql from "graphql-tag";

Next, let's add the following into our App() function:

function App() {
  return (
    <div>
    // ...
      <GraphQlLdProvider sources={[ 'https://www.rubensworks.net/' ]}>
          <Query
            query={gql`query @single(scope: all) {
              name
              image
            }`}
          >
            {({ loading, error, data }) => {
              if (loading) return <p>Loading...</p>;
              if (error) return <p>Error: {error.toString()}</p>;
              return (
                <dl>
                  <dt>Full name</dt>
                  <dd>{data.name}</dd>
                  <dt>Image</dt>
                  <dd><img src={data.image} alt={data.name} width="100px" /></dd>
                </dl>
              );
            }}
          </Query>
        </GraphQlLdProvider>
    </div>
  );
}

The code above will execute a query over https://www.rubensworks.net/ (which contains a profile that is machine-readable with RDFa) to get the name and image. If the query succeeds, the results will be rendered using HTML. If the results are still loading or an error is thrown, a different state is produced.

3.3. Viewing Your Profile

In this step, we are going to modify our hardcoded query source, and make it so that this automatically becomes the profile of the logged in user. We will also add an input field in which you can enter a custom profile to show.

Before we can do this, we will have to modify our app structure a bit so that it can maintain a state. For this, we will convert our App from a functional component into a class-based component. This can be done by converting our function into the following:

class App extends React.Component {
  render() {
    // Original function contents go here
  }
}

Try out your app again after this change, as it should be functionally the same.

Next, we will maintain the state about an active profile, and a profile that manually entered. For this, we add the following field into our class:

class App extends React.Component {
  state = { profileInput: '', activeProfile: '' };
  // ...
}

We are also going to override React's the componentDidUpdate method to trigger a state change when someone has logged in:

class App extends React.Component {
  // ...
  componentDidUpdate(prevProps) {
    const { webId } = this.props;
    if (webId && webId !== prevProps.webId)
      this.setState({ profileInput: webId });
  }
  // ...
}

Next, we add a helper method to update the state to show the profile of a given person:

class App extends React.Component {
  // ...
  viewProfile(profile) {
    this.setState({ profileInput: profile, activeProfile: profile });
  }
  // ...
}

Now we can add a profile input field to our render() method:

render() {
  const { profileInput, activeProfile } = this.state;
  return (
    <div>
    // ...
        <p>
          <label htmlFor="profile">Profile:</label>
          <input id="profile" value={profileInput}
                 onChange={e => this.setState({ profileInput: e.target.value })}/>
          <button onClick={() => this.viewProfile(profileInput)}>View</button>
        </p>
    // ...
    </div>
  );
}

One more change that is needed, is to update the GraphQlLdProvider component to use our activeProfile as source:

render() {
  const { profileInput, activeProfile } = this.state;
  return (
    <div>
    // ...
        {activeProfile &&
        <GraphQlLdProvider sources={[ activeProfile ]}>
          // Keep the existing contents of this tag as-is.
        </GraphQlLdProvider>}
    </div>
  );
}

Finally, we will modify our App so that it is always aware of the current WebID. For this, we can use the withWebId wrapper function of the Solid React components.

For this, first import withWebId:

import { LoggedIn, LoggedOut, AuthButton, Value, withWebId } from '@solid/react';

Then, at the end of your file, remove export default App; and replace it with:

export default withWebId(App);

If you now view your app, you should be able to see the profile of yourself, and select any other profile to show by entering one in the input field.

4. Linking Friends

In this final part of the tutorial, we will modify our query so that it also shows our friends, and we will make it so that you can click on friends to show their profile.

For this, let's update our query to the following:

<Query
  query={gql`query @single(scope: all) {
    name
    image
    friends @plural {
      id
      name @optional
    }
  }`}
>

This query will retrieve all friends, and return their id and name if they have one.

Next, we add the following to the HTML child of our Query component:

<dl>
  // ...
  <dd>
    <ul>
      {
        data.friends && data.friends.map(friend =>
          <li key={friend.id}>
            <button onClick={() => this.viewProfile(friend.id)}>
              {friend.name || friend.id}
            </button>
          </li>)
      }
    </ul>
  </dd>
</dl>

This will iterate over all friends, and show them in a list, together with a button that will cause their profile to be shown.

You (should) now have a fully functional Solid profile viewer! Feel free to modify the GraphQL-LD query by adding more fields to show from your profile. The default context lists all available fields, and the graphql-to-sparql README shows all possible GraphQL-LD features.