Getting Started: Part 2

This tutorial creates an introductory application that uses Inrupt’s JavaScript client libraries to write to your Pod. 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.

Locally Run Getting Started Application Part 2

Prerequisites

The tutorial follows Getting Started: Part 1. As such, the tutorial assumes that you have:

  • Registered a Pod.

  • Installed npm.

If you do not have the prerequisites, refer to Prerequisites in Getting Started: Part 1.

Note

The tutorial uses either https://pod.inrupt.com or https://inrupt.net/ as the example Pod Server. If your example Pod is not on either of these 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.

Build the Application

1. Initialize the Application

  1. Create the directory structure for your Webpack project:

    mkdir -p  my-demo-app2 my-demo-app2/src my-demo-app2/dist
    
  2. Go to the newly created my-demo-app2 directory.

    cd my-demo-app2
    
  3. 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
      
      1. You can either hit return to accept the default values (including empty values) or supply your own values.

      2. When prompted Is this OK? (yes), enter to accept yes.

2. Install the Client Libraries and Polyfill

  1. Use npm to install the solid-client, solid-client-authn-browser, and vocab-common-rdf libraries:

    npm install @inrupt/solid-client @inrupt/solid-client-authn-browser @inrupt/vocab-common-rdf
    
  2. For the browser application, install the polyfill for buffer:

    npm install buffer
    

3. Install Webpack

  1. Use npm to install Webpack packages:

    npm install webpack webpack-cli webpack-dev-server css-loader style-loader --save-dev
    
  2. In my-demo-app2 directory, create a webpack.config.js file with the following content:

    const path = require("path");
    module.exports = {
       mode: "development",
       entry: "./src/index.js",
       output: {
         path: path.resolve(__dirname, "dist"), 
         filename: "index.js" 
       },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [ { loader: "style-loader" }, { loader: "css-loader" } ],
          },
        ]
      },
      devServer: {
        static: "./dist"
      }
    };
    
  3. In my-demo-app2 directory, edit the package.json file to add build and start script fields to scripts:

    Tip

    Be sure to add the comma after the preceding field value before adding the build and start fields.

    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "build": "webpack",
      "start": "webpack serve --open"
    },
    

4. Create the Application

In the my-demo-app2 directory, create the files for the application. The tutorial provides an explanation of the JavaScript code at the end of the tutorial.

  1. In the my-demo-app2/dist folder, create a my-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;
    }
    
    #labelStatus[role="alert"] {
       padding-left: 1rem;
       color: purple;
    }
    
    
    #write {
      background: #e6f4f9;
    }
    
    #titles {
       margin-left: 1.3rem;
    }
    
    label {
       vertical-align: top;
    }
    #savedtitles {
       border: none;
       background: #f8fbfd;
       margin-left: 6rem;
    }
    
    #labelRetrieved {
       margin-left: 1.2rem;
    }
    
  2. In the my-demo-app2/dist, create an index.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: Write Operation</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="write" class="panel" >
          <div class="row">
            <label id="writelabel">2. Create a private reading list in your Pod.</label>
          </div>
          <div class="row">
            <label id="podlabel" for="PodURL">a. Enter your Pod URL: </label>
            <input type="url" id="PodURL" name="PodURL"  size="50" pattern="https://.*"  placeholder="e.g., https://pod.inrupt.com/<username>">/getting-started/readingList/myList
          </div>
          <div class="row">
            <label id="listLabel" for="titles">b. Enter items to read: </label>
            <textarea id="titles" name="titles" rows="5" cols="42" >
    Leaves of Grass
    RDF 1.1 Primer</textarea>
            <button name="btnCreate" id="btnCreate">Create</button> <span id="labelCreateStatus"></span>
          </div>
          <div class="row"></div>
            <label id="labelRetrieved" for="savedtitles">Retrieved:</label>
            <textarea id="savedtitles" name="savedtitles" rows="5" cols="42" readonly></textarea>
          </div>
        </div>
      </body>
    </html>
    
  3. In the my-demo-app2/src, create an index.js file with the following content:

    Note

    If your Pod is not on https://pod.inrupt.com, modify the oidcIssuer value.

    import {
      createSolidDataset,
      createThing,
      setThing,
      addUrl,
      addStringNoLocale,
      saveSolidDatasetAt,
      getSolidDataset,
      getThingAll,
      getStringNoLocale,
      removeThing,
      FetchError
    } from "@inrupt/solid-client";
    
    import {
      login,
      handleIncomingRedirect,
      getDefaultSession,
      fetch
    } from "@inrupt/solid-client-authn-browser";
    
    import { SCHEMA_INRUPT, RDF, AS } from "@inrupt/vocab-common-rdf";
    
    const buttonLogin = document.querySelector("#btnLogin");
    const buttonCreate = document.querySelector("#btnCreate");
    buttonCreate.disabled=true;
    const labelCreateStatus = document.querySelector("#labelCreateStatus");
    
    // 1a. Start Login Process. Call login() function.
    function startLogin() {
      return login({
        oidcIssuer: "https://broker.pod.inrupt.com",
        redirectUrl: window.location.href,
        clientName: "Getting started app"
      });
    }
    
    // 1b. Login Redirect.
    // When redirected after login, call handleIncomingRedirect() function to
    // finish the login process by retrieving session information.
    async function finishLogin() {
        await handleIncomingRedirect();
        const session = getDefaultSession();
        if (session.info.isLoggedIn) {
          // Update the page with the status.
          document.getElementById("labelStatus").textContent = `Logged in with WebID ${session.info.webId}`;
          document.getElementById("labelStatus").setAttribute("role", "alert");
          // Enable Create button
          buttonCreate.disabled=false;
        }
    }
    
    // The example has the login redirect back to the index.html.
    // finishLogin() 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.
    finishLogin();
    
    // 2. Create the Reading List
    async function createList() {
      labelCreateStatus.textContent = "";
      const podUrl = document.getElementById("PodURL").value;
     
      // For simplicity and brevity, this tutorial hardcodes the SolidDataset URL.
      // In practice, you should add a link to this resource in your profile that applications can follow.
      const readingListUrl = `${podUrl}/getting-started/readingList/myList`;
     
      let titles = document.getElementById("titles").value.split("\n");
    
      // Fetch or create a new reading list.
      let myReadingList;
    
      try {
        // Attempt to fetch the reading list in case it already exists.
        myReadingList = await getSolidDataset(readingListUrl, { fetch: fetch });
        // Clear the list to override the whole list
        let titles = getThingAll(myReadingList);
        titles.forEach(title => {
         myReadingList = removeThing(myReadingList, title);
        });
      } catch (error) {
        if (typeof error.statusCode === "number" && error.statusCode === 404) {
          // if not found, create a new SolidDataset (i.e., the reading list)
          myReadingList = createSolidDataset();
        } else {
          console.error(error.message);
        }
      }
    
      // Add titles to the Dataset
      for (let i = 0; i < titles.length; i++) {
        let title = createThing({name: "title" + i});
        title = addUrl(title, RDF.type, AS.Article);
        title = addStringNoLocale(title, SCHEMA_INRUPT.name, titles[i]);
        myReadingList = setThing(myReadingList, title);
      }
    
      try {
         
        // Save the SolidDataset 
        let savedReadingList = await saveSolidDatasetAt(
          readingListUrl,
          myReadingList,
          { fetch: fetch }
        );
    
        labelCreateStatus.textContent = "Saved";
    
        // Refetch the Reading List
        savedReadingList = await getSolidDataset(
          readingListUrl,
          { fetch: fetch }
        );
    
        let items = getThingAll(savedReadingList);
    
        let listcontent="";
        for (let i = 0; i < items.length; i++) {
          let item = getStringNoLocale(items[i], SCHEMA_INRUPT.name);
          if (item !== null) {
              listcontent += item + "\n";
          }
        }
    
        document.getElementById("savedtitles").value = listcontent;
    
      } catch (error) {
        console.log(error);
        labelCreateStatus.textContent = "Error" + error;
        labelCreateStatus.setAttribute("role", "alert");
      } 
    }
    
    buttonLogin.onclick = function() {  
      startLogin();
    };
    
    buttonCreate.onclick = function() {  
      createList();
    };
    

    For details about the JavaScript code, see Examination of the Code.

5. Run the Application

  1. In the my-demo-app2 directory, run:

    npm run build && npm run start
    

    The output resembles the following:

    ℹ 「wds」: Project is running at http://localhost:8080/
    ...
    ℹ 「wdm」: Compiled successfully.
    
  2. Open localhost:8080 in a browser.

  3. Login.

    1. Click Login to login.

    2. 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 approve this application’s access to the requested information. To continue, click Approve.

      For inrupt.net, you will be prompted to authorize applications from http://localhost:8080 to access your Pod, if you have not done so already.

    3. You are redirected back to your page.

    Tip

    You must be logged in to enable the Create button.

  4. Write your reading list.

    1. Enter your Pod URL; e.g.,

      • https://pod.inrupt.com/<username>

      • https://<username>.inrupt.net.

    2. Add your reading list.

    3. Click Create to create.

    The reading list is created in your <PodURL>/getting-started/readingList/myList.

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.

Start the login process by calling login():

  • To start login for a Pod on https://pod.inrupt.com:

    function startLogin() {
      return login({
        oidcIssuer: "https://broker.pod.inrupt.com",
        redirectUrl: window.location.href,
        clientName: "Getting started app"
      });
    }
    

    This function sends the user to the identity provider oidcIssuer, and once logged in, redirects back to the specified redirectURL.

  • To start login for a Pod on https://inrupt.net:

    function startLogin() {
      return login({
        oidcIssuer: "https://inrupt.net",
        redirectUrl: window.location.href,
        clientName: "Getting started app"
      });
    }
    

    This function sends the user to the identity provider oidcIssuer, and once logged in, redirects back to the specified redirectURL.

Note

If your Pod is not on either https://pod.inrupt.com or https://inrupt.net, modify the oidcIssuer value.

For more information on using the library to authenticate, see Authentication.

Write Reading List

The example uses the solid-client and vocab-common-rdf libraries to write data to your Pod.

Tip

For the sake of simplicity and brevity, this getting started guide hardcodes the SolidDataset URL. In practice, you should add a link to this resource in your profile that applications can follow.

From @inrupt/solid-client and @inrupt/vocab-common-rdf, import the objects used in the application:

import {
  createSolidDataset,
  createThing,
  setThing,
  addUrl,
  addStringNoLocale,
  saveSolidDatasetAt,
  getSolidDataset,
  getThingAll,
  getStringNoLocale,
  removeThing,
  FetchError
} from "@inrupt/solid-client";
  • For more information on property identifiers and vocabularies, see Vocabulary.

  • For more information on write operations, see Write Data.

Additional Information

React SDK Availability

To create a sample application using Inrupt’s Solid React SDK, refer to the Solid React SDK documentation.