Getting Started: Part 1¶
This tutorial creates an introductory application that uses Inrupt’s JavaScript client libraries to read your user profile data. Alternatively, to create a sample application using Inrupt’s Solid React SDK, refer to Solid React SDK documentation.
The tutorial uses npm and Webpack to run the application locally on
localhost:8080
.

Prerequisites¶
Register Your Pod and Create Your Profile¶
Note
The tutorial uses either https://pod.inrupt.com
or
https://inrupt.net/
for your example Pod Server. If your
example Pod is not on either of those servers, you must change the
oidcIssuer
value in the application’s index.js
file.
For more information on these Pod servers, see Inrupt Pod Spaces.
To create a Pod at https://pod.inrupt.com
(Currently in Alpha),
To login or sign up, you must agree to the Inrupt terms of use.
Click Login/Sign Up. You are shown the Login screen.
Click the Sign up link at the bottom to switch to the Sign up screen.
Fill in your username, email, and password.
Click Sign up. You are sent a verification code to the entered email.
Retrieve the code from the email. Enter the code and click Confirm Account.
Click Authorize to allow “Inrupt Pod Space Registration” to create your account.
Once authorized, your Pod is created.
WebId:
https://pod.inrupt.com/<username>/profile/card#me
Pod URL:
https://pod.inrupt.com/<username>/
Click on the link to Go to PodBrowser.
At the Pod Browser login screen, enter
https://broker.pod.inrupt.com
.Click Authorize to allow PodBrowser to access your Pod.
Once logged into PodBrowser, click on the top right person icon and click Profile.
Edit your public profile.
Important
Your profile information is public.
For this tutorial:
Enter a name in the Name field.
Enter a role in the Role field.
Your data is saved automatically.
Log out. Move your mouse to the image in the upper-right corner image to display additional menu items and click Log out.
To create a Pod at https://inrupt.net/
:
Open a browser to https://inrupt.net/.
Click Register.
Fill in the form and click Register.
You are redirected to your public Solid Pod URL:
https://<yourusername>.inrupt.net
.Click Log in on the upper right corner.
From the list of identity providers, click on your Solid Pod URL.
Enter your Username and Password and click Log in.
Click on the Edit your profile tab.
Tip
You may need to reload the page.
Edit your public profile.
Important
Your profile information is public.
For this tutorial:
Enter a name in the Name field.
Enter a role in the Role field.
Your data is saved automatically.
Log out. Move your mouse to the image in the upper-right corner image to display additional menu items and click Log out.
Install npm
¶
If you do not already have npm installed, install npm. npm
is installed as part of the
Node.js installation.
Please make sure that you use a version of Node that is supported by Webpack. At the time of writing, that is version 10 or later.
Build the Application¶
1. Initialize the Application¶
Create the directory structure for your Webpack project:
mkdir -p my-demo-app my-demo-app/src my-demo-app/dist
Go to the newly created
my-demo-app
directory.cd my-demo-app
Initialize the application.
To accept the default values for the application without prompts:
npm init -y
- or -
To be prompted to enter values for the application:
npm init
You can either hit return to accept the default values (including empty values) or supply your own values.
When prompted
Is this OK? (yes)
, enter to acceptyes
.
2. Install the Client Libraries¶
Use
npm
to install thesolid-client
,solid-client-authn-browser
, andvocab-common-rdf
libraries:npm install @inrupt/solid-client @inrupt/solid-client-authn-browser @inrupt/vocab-common-rdf
3. Install Webpack¶
Use
npm
to install Webpack packages:npm install webpack webpack-cli webpack-dev-server css-loader style-loader --save-dev
In
my-demo-app
directory, create awebpack.config.js
file with the following content:const path = require("path"); module.exports = { mode: "development", entry: "./src/index.js", output: { path: path.resolve(__dirname, "public"), filename: "index.js" }, module: { rules: [ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader" } ], }, ] }, devServer: { contentBase: "./dist" }, resolve: { fallback: { stream: require.resolve("stream-browserify") , crypto: require.resolve("crypto-browserify") } } };
In
my-demo-app
directory, edit thepackage.json
file to addbuild
andstart
script fields toscripts
:Tip
Be sure to add the comma after the preceding field value before adding the
build
andstart
fields."scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "start": "webpack serve --open true" },
4. Create the Application¶
In the my-demo-app
directory, create the files for the application.
The tutorial provides an explanation of the JavaScript code at the end of the
tutorial.
In the
my-demo-app/dist
folder, create amy-demo.css
file with the following content:h2,h3 { margin: 1rem 1.2rem 1rem 1.4rem; } body * { margin-left: .5rem; margin-right: 1rem; } header { border-bottom: #5795b9 solid; padding-left: .5rem; } .panel { border: 1px solid #005b81; border-radius: 4px; box-shadow: rgb(184, 196, 194) 0px 4px 10px -4px; box-sizing: border-box; padding: 1rem 1.5rem; margin: 1rem 1.2rem 1rem 1.2rem; } #login { background: white; } #read { background: #e6f4f9; } #labelStatus[role="alert"] { padding-left: 1rem; color: purple; } .display { margin-left: 1rem; color: gray; } dl { display: grid; grid-template-columns: max-content auto; } dt { grid-column-start: 1; } dd { grid-column-start: 2; }
In the
my-demo-app/dist
, create anindex.html
file with the following content:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Getting Started: Inrupt JavaScript Client Libraries</title> <script defer src="./index.js" ></script> <link rel="stylesheet" href="my-demo.css" /> </head> <body> <header> <h2>Getting Started</h2> <h3>with Inrupt JavaScript Client Libraries</h3> </header> <section id="login" class="panel"> <div class="row"> <label id="labelLogin" for="btnLogin">1. Click the button to log in: </label> <button name="btnLogin" id="btnLogin">Login</button> <p id="labelStatus"></p> </div> </section> <div id="read" class="panel" > <div class="row"> <label id="readlabel" for="webID">2. Enter a WebID: </label> <input type="url" id="webID" name="webID" size="50" pattern="https://.*" value="https://pod.inrupt.com/<username>/profile/card#me"> <button name="btnRead" id="btnRead" >Read Profile</button> </div> <dl class="display"> <dt>Formatted Name (FN): </dt> <dd id="labelFN"></dd> <dt>Role: </dt> <dd id="labelRole"></dd> </dl> </div> </body> </html>
In the
my-demo-app/src
, create anindex.js
file with the following content:Note
If your Pod is not on
https://pod.inrupt.com
, modify theoidcIssuer
value.import { getSolidDataset, getThing, getStringNoLocale } from "@inrupt/solid-client"; import { Session } from "@inrupt/solid-client-authn-browser"; import { VCARD } from "@inrupt/vocab-common-rdf"; const session = new Session(); const buttonLogin = document.querySelector("#btnLogin"); const buttonRead = document.querySelector("#btnRead"); // 1a. Start Login Process. Call session.login() function. async function login() { if ( !session.info.isLoggedIn ) { await session.login({ oidcIssuer: "https://broker.pod.inrupt.com", redirectUrl: window.location.href, }); } } // 1b. Login Redirect. Call session.handleIncomingRedirect() function. // When redirected after login, finish the process by retrieving session information. async function handleRedirectAfterLogin() { await session.handleIncomingRedirect(window.location.href); if (session.info.isLoggedIn) { // Update the page with the status. document.getElementById("labelStatus").textContent = "Your session is logged in."; document.getElementById("labelStatus").setAttribute("role", "alert"); } } // The example has the login redirect back to the index.html. // This calls the function to process login information. // If the function is called when not part of the login redirect, the function is a no-op. handleRedirectAfterLogin(); // 2. Read profile async function readProfile() { const webID = document.getElementById("webID").value; // Profile is public data; i.e., you do not need to be logged in to read the data. // For illustrative purposes, shows both an authenticated and non-authenticated reads. let myDataset; if (session.info.isLoggedIn){ myDataset = await getSolidDataset(webID, { fetch: session.fetch }); } else { myDataset = await getSolidDataset(webID); } const profile = getThing(myDataset, webID); // Get the formatted name (fn) using the property identifier "http://www.w3.org/2006/vcard/ns#fn". // VCARD.fn object is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#fn". // As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#fn" string instead of VCARD.fn. const fn = getStringNoLocale(profile, VCARD.fn); // VCARD.role obect is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#role" // As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#role" string instead of VCARD.role. const role = getStringNoLocale(profile, VCARD.role); // Update the page with the retrieved values. document.getElementById("labelFN").textContent = fn; document.getElementById("labelRole").textContent = role; } buttonLogin.onclick = function() { login(); }; buttonRead.onclick = function() { readProfile(); };
In the
my-demo-app/dist
folder, create amy-demo.css
file with the following content:h2,h3 { margin: 1rem 1.2rem 1rem 1.4rem; } body * { margin-left: .5rem; margin-right: 1rem; } header { border-bottom: #5795b9 solid; padding-left: .5rem; } .panel { border: 1px solid #005b81; border-radius: 4px; box-shadow: rgb(184, 196, 194) 0px 4px 10px -4px; box-sizing: border-box; padding: 1rem 1.5rem; margin: 1rem 1.2rem 1rem 1.2rem; } #login { background: white; } #read { background: #e6f4f9; } #labelStatus[role="alert"] { padding-left: 1rem; color: purple; } .display { margin-left: 1rem; color: gray; } dl { display: grid; grid-template-columns: max-content auto; } dt { grid-column-start: 1; } dd { grid-column-start: 2; }
In the
my-demo-app/dist
, create anindex.html
file with the following content:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Getting Started: Inrupt JavaScript Client Libraries</title> <script defer src="./index.js" ></script> <link rel="stylesheet" href="my-demo.css" /> </head> <body> <header> <h2>Getting Started</h2> <h3>with Inrupt JavaScript Client Libraries</h3> </header> <section id="login" class="panel"> <div class="row"> <label id="labelLogin" for="btnLogin">1. Click the button to log in: </label> <button name="btnLogin" id="btnLogin">Login</button> <p id="labelStatus"></p> </div> </section> <div id="read" class="panel" > <div class="row"> <label id="readlabel" for="webID">2. Enter a WebID: </label> <input type="url" id="webID" name="webID" size="50" pattern="https://.*" value="https://docs-example.inrupt.net/profile/card#me"> <button name="btnRead" id="btnRead" >Read Profile</button> </div> <dl class="display"> <dt>Formatted Name (FN): </dt> <dd id="labelFN"></dd> <dt>Role: </dt> <dd id="labelRole"></dd> </dl> </div> </body> </html>
In the
my-demo-app/src
, create anindex.js
file with the following content:Note
If your Pod is not on
https://inrupt.net
, modify theoidcIssuer
value.import { getSolidDataset, getThing, getStringNoLocale } from "@inrupt/solid-client"; import { Session } from "@inrupt/solid-client-authn-browser"; import { VCARD } from "@inrupt/vocab-common-rdf"; const session = new Session(); const buttonLogin = document.querySelector("#btnLogin"); const buttonRead = document.querySelector("#btnRead"); // 1a. Start Login Process. Call session.login() function. async function login() { if ( !session.info.isLoggedIn ) { await session.login({ oidcIssuer: "https://inrupt.net", redirectUrl: window.location.href, }); } } // 1b. Login Redirect. Call session.handleIncomingRedirect() function. // When redirected after login, finish the process by retrieving session information. async function handleRedirectAfterLogin() { await session.handleIncomingRedirect(window.location.href); if (session.info.isLoggedIn) { // Update the page with the status. document.getElementById("labelStatus").textContent = "Your session is logged in."; document.getElementById("labelStatus").setAttribute("role", "alert"); } } // The example has the login redirect back to the index.html. // This calls the function to process login information. // If the function is called when not part of the login redirect, the function is a no-op. handleRedirectAfterLogin(); // 2. Read profile async function readProfile() { const webID = document.getElementById("webID").value; // Profile is public data; i.e., you do not need to be logged in to read the data. // For illustrative purposes, shows both an authenticated and non-authenticated reads. let myDataset; if (session.info.isLoggedIn){ myDataset = await getSolidDataset(webID, { fetch: session.fetch }); } else { myDataset = await getSolidDataset(webID); } const profile = getThing(myDataset, webID); // Get the formatted name (fn) using the property identifier "http://www.w3.org/2006/vcard/ns#fn". // VCARD.fn object is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#fn". // As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#fn" string instead of VCARD.fn. const fn = getStringNoLocale(profile, VCARD.fn); // VCARD.role obect is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#role" // As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#role" string instead of VCARD.role. const role = getStringNoLocale(profile, VCARD.role); // Update the page with the retrieved values. document.getElementById("labelFN").textContent = fn; document.getElementById("labelRole").textContent = role; } buttonLogin.onclick = function() { login(); }; buttonRead.onclick = function() { readProfile(); };
For details about the JavaScript code, see Examination of the Code.
5. Run the Application¶
In the
my-demo-app
directory, run:npm run build && npm run start
The output resembles the following:
ℹ 「wds」: Project is running at http://localhost:8080/ ... ℹ 「wdm」: Compiled successfully.
Open
localhost:8080
in a browser.Login.
Click Login to login.
If you have logged out of your Pod, you are prompted to log in. Enter your username and password and log in.
For pod.inrupt.com, you will be prompted to accept Inrupt Terms and Conditions and authorize this application. To continue, click Authorize.
For inrupt.net, you will be prompted to authorize applications from
http://localhost:8080
to access your Pod.You are redirected back to your page.
Read public profile data.
Note
Because the profile data is public, you do not need to log in to read the data. The application does so to show how to make authenticated read requests.
Enter your WebID.
Click Read Profile.
The page should display your formatted name and role.
You can read public profiles of others by entering in their WebID; e.g.,
https://docs-example.inrupt.net/profile/card#me
.
6. Exit the Application¶
To exit the application, stop the npm run start
process; e.g.,
Ctrl-C
.
Examination of the Code¶
Login Code¶
The example uses the solid-client-authn-browser
library to log in.
solid-client-authn-browser
is for client-side code only.
From @inrupt/solid-client-authn-browser
, import Session
:
import { Session } from "@inrupt/solid-client-authn-browser";
Create a new Session:
const session = new Session();
Start the login process by calling Session.login():
To login to a Pod on
https://pod.inrupt.com
:// 1a. Start Login Process. Call session.login() function. async function login() { if ( !session.info.isLoggedIn ) { await session.login({ oidcIssuer: "https://broker.pod.inrupt.com", redirectUrl: window.location.href, }); } }
To login to a Pod on
https://inrupt.net
:// 1a. Start Login Process. Call session.login() function. async function login() { if ( !session.info.isLoggedIn ) { await session.login({ oidcIssuer: "https://inrupt.net", redirectUrl: window.location.href, }); } }
This function sends the user to the identity provider
oidcIssuer
, and once logged in, redirects back to the specifiedredirectURL
.Note
If your Pod is not on
pod.inrupt.net
orhttps://inrupt.net
, modify theoidcIssuer
value accordingly.
When redirected back from the identity provider, use Session.handleIncomingRedirect() to complete the login process:
// 1b. Login Redirect. Call session.handleIncomingRedirect() function.
// When redirected after login, finish the process by retrieving session information.
async function handleRedirectAfterLogin() {
await session.handleIncomingRedirect(window.location.href);
if (session.info.isLoggedIn) {
// Update the page with the status.
document.getElementById("labelStatus").textContent = "Your session is logged in.";
document.getElementById("labelStatus").setAttribute("role", "alert");
}
}
// The example has the login redirect back to the index.html.
// This calls the function to process login information.
// If the function is called when not part of the login redirect, the function is a no-op.
handleRedirectAfterLogin();
The function collects the information provided by the identity provider.
For more information on using the library to authenticate, see Authenticate.
Read Profile Code¶
The example uses the solid-client
and vocab-common-rdf
libraries to read profile data from a Pod.
From @inrupt/solid-client
and @inrupt/vocab-common-rdf
,
import the objects used in the application:
import {
getSolidDataset,
getThing,
getStringNoLocale
} from "@inrupt/solid-client";
import { VCARD } from "@inrupt/vocab-common-rdf";
Use getSolidDataset to fetch the SolidDataset from the URI.
// Profile is public data; i.e., you do not need to be logged in to read the data.
// For illustrative purposes, shows both an authenticated and non-authenticated reads.
let myDataset;
if (session.info.isLoggedIn){
myDataset = await getSolidDataset(webID, { fetch: session.fetch });
} else {
myDataset = await getSolidDataset(webID);
}
For authenticated reads, pass in the URI and the session’s fetch function as an option.
For unauthenticated reads, pass in the URI.
Although the application uses a URI that contains a fragment (i.e.
the URI of a Thing), the returned SolidDataset is the resource
located at the URI w/o the fragment. That is, if the URI is
https://<username>.inrupt.net/profile/card#me
, the returned
SolidDataset is the resource at
https://<username>.inrupt.net/profile/card
.
You can modify the application to pass in the resource URI
https://<username>.inrupt.net/profile/card
instead.
From the fetched SolidDataset, the application uses the
getThing
to retrieve the profile. A Thing’s URI contains a fragment, as in
#me
in
https://pod.inrupt.com/<username>/profile/card#me
or
https://<username>.inrupt.net/profile/card#me
.
const profile = getThing(myDataset, webID);
The application uses getStringNoLocale to retrieve:
the formatted name (
fn
) property stored with the identifierhttp://www.w3.org/2006/vcard/ns#fn
, andthe role (
role
) property stored with the identifierhttp://www.w3.org/2006/vcard/ns#role
.
// Get the formatted name (fn) using the property identifier "http://www.w3.org/2006/vcard/ns#fn".
// VCARD.fn object is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#fn".
// As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#fn" string instead of VCARD.fn.
const fn = getStringNoLocale(profile, VCARD.fn);
// VCARD.role obect is a convenience object that includes the identifier string "http://www.w3.org/2006/vcard/ns#role"
// As an alternative, you can pass in the "http://www.w3.org/2006/vcard/ns#role" string instead of VCARD.role.
const role = getStringNoLocale(profile, VCARD.role);
The application uses VCARD.fn
and VCARD.role
convenience
objects from the vocab-common-rdf
library to specify the
property identifiers.
For more information on property identifiers and vocabularies, see Vocabulary.
For more information on read operations, see Read/Write Data.
Additional Information¶
API Documentation¶
React SDK Availability¶
To create a sample application using Inrupt’s Solid React SDK, refer to the Solid React SDK documentation.