Use GraphQL with React and Oracle Content Management

Introduction

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.

In this tutorial, we’ll show how to use GraphQL with React connected to Oracle Content Management. In particular, we’ll focus on the existing ‘People’ page on the React minimal sample site by querying the necessary data using GraphQL and Apollo client. The other pages (‘Home’ and ‘Contact Us’) are currently using a REST API.

Prerequisites

Before proceeding with this tutorial, we recommend that you read the following information first:

To follow this tutorial, you’ll need:

What We’re Building

With the minimal site built in React, you can easily retrieve images and other content from your Oracle Content Management repository.

To take a look at what we’re building, here’s the end state of our tutorial, a basic React minimal site that consumes content from Oracle Content Management:

https://headless.mycontentdemo.com/samples/oce-react-minimal-sample

This tutorial will focus solely on the ‘People’ page of this site.

This is what the People page will look like at the end of this tutorial:

This image shows the contact us page for an React minimal site.

To proceed, you’ll need to have an active subscription to Oracle Content Management and be logged in with the Content Administrator role.

Task 1: Understand the People Page Taxonomy

The downloadable asset pack contains an asset called Minimal Main GraphQL with the slug ‘minimalmaingraphql’.

The Minimal Main GraphQL asset contains a page of type PeoplePage called People. This is what it looks like:

This image shows the Minimal Main GraphQL taxonomy.

The People asset is of type PeoplePage, and it contains subtypes of people. This is what it looks like:

This image shows the People page taxonomy.

This is what an example people asset looks like (note all the metadata in the Attributes panel):

This image shows the people taxonomy.

Task 2: Use Apollo Client

To query these assets from Oracle Content Management using GraphQL, we need add the “@apollo/client” dependency in the package.json file.

The endpoint for the GraphQL API is the server URL concatenated with /content/published/api/v1.1/graphql, and is defined in the src/scripts/services-graphql.js file.

const SERVER_URL_GRAPHQL = `${process.env.SERVER_URL}/content/published/api/v1.1/graphql`;

Task 3: Run and Process the GraphQL query

The src/pages/Routes.js file defines the routes for the application. The /page/people path is directed to the People component.

export default [
  {
    ...App,
    routes: [
      {
        ...People,
        path: '/page/people',
        exact: true,
        title: 'People',
      },
      {
        ...Page,
        path: '/page/:slug',
        exact: true,
        title: 'Page',
      },
    ],
  },
];

The People component is responsible for rendering the People Page. Open the People component, located at src/pages/People.jsx.

The fetchInitialData function is called during server-side rendering to get the data from the server.

import fetchPeople from '../scripts/services-graphql';

function fetchInitialData(req) {
  const pageslug = req.path.split('/').pop();
  return fetchPeople(pageslug);
}

The componentDidUpdate function checks if the location pathname has changed, which indicates a navigation change, and then fetches data accordingly.

  componentDidUpdate() {
    const { pageSlug } = this.state;
    const { location } = this.props;
    const { pathname } = location;
    const newSlug = pathname.split('/').pop();
    if (pageSlug !== newSlug) {
      this.fetchData(newSlug);
    }
  }
  // Client Side Data Fetching: called from Client when doing client side routing/hydration
  async fetchData(slug) {
    const pageData = await fetchPeople(slug);
    document.title = pageData.name;
    this.setState(() => ({
      pageData,
      pageSlug: slug,
    }));
  }

In the render() method, you can see that the data to render is obtained from the component’s state. This data has an announcement section and a list of persons, so the People component’s render() method iterates through the list and calls the Person component for each person in the list.

The People component exports an object containing the fetchInitialData function and the component.

export default {
  fetchInitialData,
  component:  People,
};

The fetchPeople method is defined in the src/scripts/services-graphql.js file and uses the Apollo client to make the query below to obtain all the data for the People page.

More information about how to query data using Oracle Content Management can be found in the documentation.

const GET_PEOPLE_PAGE = gql`
  query ($peopleSlug: String!, $channelToken: String!){
    getPeoplePage(slug: $peopleSlug, channelToken: $channelToken) {
      id
      slug
      name
      fields {
        announcement {
          id
          fields {
            type: fieldType
            heading
            body
            actions
            image {
              ...sectionImages
            }
          }
        }
        people {
          id
          fields {
            fullname
            title
            biodata
            file {
              metadata {
                width
                height
              }
            }
            renditions {
              name
              format
              file {
                url
                metadata {
                  height
                  width
                }
              }
            }
          }
        }
      }
    }
  }
  fragment sectionImages on image {
    id
    fields {
      file {
        metadata {
          width
          height
        }
      }
      renditions {
        name
        format
        file {
          url
          metadata {
            height
            width
          }
        }
      }
    }
  }`;

export default async function fetchPeople(peopleSlug) {
  // Get the page details
  const channelToken = `${process.env.CHANNEL_TOKEN}`;
  const client = new ApolloClient(
    {
      link: new HttpLink({ uri: SERVER_URL_GRAPHQL, fetch }),
      cache: new InMemoryCache(),
    },
  );
  const page = await client.query({
    query: GET_PEOPLE_PAGE,
    variables: { peopleSlug, channelToken },
  });

  const pageData = process.env.IS_BROWSER
    ? page.data.getPeoplePage
    : JSON.parse(JSON.stringify(page.data.getPeoplePage));
  const { people, announcement } = pageData.fields;
  if (announcement.fields.image) {
    announcement.renditionURLs = getSourceSetGraphQL(announcement.fields.image);
  }
  if (people) {
    people.forEach((person) => {
      person.renditionURLs = getSourceSetGraphQL(person);
    });
  }
  return pageData;
}

The returned result is then processed to create the HTML to display the People page. The GraphQL IDE is available at http://your_instance/content/published/api/v1.1/graphql/explorer.

This image shows the explorer.

Preview API content using GraphQL

GraphQL support for Content Preview is also available as well and is fairly simple to implement. More information about using the preview API with GraphQL can be found here.

For preview calls using the content SDK, the only thing needed is to create the appropriate client as specified in the file server-config-utils.js

if (process.env.PREVIEW === 'true') {
  clientInstance = createPreviewClient(serverconfig);
} else {
  clientInstance = createDeliveryClient(serverconfig);
}

For graphql calls we need to specify the correct endpoint as specified in the file services-graphql.js

let serverUrlGraphQL = isPreview
  ? '/content/preview/api/v1.1/graphql'
  : `${process.env.SERVER_URL}/content/published/api/v1.1/graphql`;

if (hostUrl != null && isPreview) {
  serverUrlGraphQL = `${hostUrl}/content/preview/api/v1.1/graphql`;
}

To switch over to using preview mode, the change is as simple as updating the .env file for the project to include additional variables. A complete guide about preview mode settings and how to obtain these environment variables for your instance can be found here. Most notably, you will need to set PREVIEW, OPTIONS, and AUTH/AUTH_PARAMS in the .env file according to the linked documentation for setting the AUTH/AUTH_PARAMS for your specific instance.

Conclusion

In this tutorial, we added a ‘People’ page to the React minimal sample site using GraphQL to obtain the data for that page. The other pages (‘Home’ and ‘Contact Us’) still use the REST API.

Additional information on GraphQL in Oracle Content Management can be found in the documentation.