-
Notifications
You must be signed in to change notification settings - Fork 0
Tutorial walkthrough
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.
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
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.
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.
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.
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:
-
solid-react-graphql-ld
: React components for executing GraphQL-LD queries easily. -
graphql-tag
: Parses GraphQL queries to an Abstract Syntax Tree.
This can be done by executing the following:
npm install solid-react-graphql-ld graphql-tag
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 givenquery
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.
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.
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.