Implementing Content Hub One Item APIs in a React App Using Client Credentials and OAuth2
Content Hub One by Sitecore is a powerful headless CMS designed for managing and delivering content through APIs. One of its key features is the Content Hub One Item API, which allows developers to retrieve content items in a structured way. When integrating Content Hub One Item APIs into a React app, it’s essential to manage authentication, especially when using a secure method like OAuth2 with client credentials.
This blog will walk through the process of setting up a React app to use Content Hub One Item APIs with OAuth2 authentication via client credentials. You’ll learn how to handle authentication, fetch content from the API, and display it in your app.
Prerequisites
Before diving into the implementation, ensure that you have the following:
- A basic understanding of React and its components.
- Access to a Content Hub One environment.
- OAuth2 credentials (Client ID, Client Secret) for the Content Hub API.
- Familiarity with OAuth2 authentication flow using client credentials.
Setting Up the React App
- Create a New React App:
Start by setting up a new React application usingcreate-react-app.
- Install
axios
to help with HTTP requests. - Install React Router.
npx create-react-app content-hub-item-api-services
cd content-hub-item-api-services
npm install axios
npm install react-router-dom
Set-up Environment Variables
Create a .env
file in the root of your project.
REACT_APP_AUTH_URL= https://YOUR_AUTH_PROVIDER/oauth/token
REACT_APP_CLIENT_ID= YOUR_CLIENT_ID
REACT_APP_CLIENT_SECRET= YOUR_CLIENT_SECRET
REACT_APP_AUDIENCE= YOUR_APP_AUDIENCE
REACT_APP_CONTENT_HUB_API_URL= https://YOUR_API_BASE_URL/api/content/v1
Create a Service to Obtain Access Token (Client Credentials Flow) and Fetch Data from Content Hub One
Create a file contentHubService.js
to manage API calls.
// src/contentHubService.js
import axios from 'axios';
const AUTH_URL = process.env.REACT_APP_AUTH_URL;
const CLIENT_ID = process.env.REACT_APP_CLIENT_ID;
const CLIENT_SECRET = process.env.REACT_APP_CLIENT_SECRET;
const AUDIENCE = process.env.REACT_APP_AUDIENCE;
const CONTENT_HUB_API_URL = process.env.REACT_APP_CONTENT_HUB_API_URL;
// Function to get an access token using the client credentials flow
export const getAccessToken = async () => {
try {
const response = await axios.post(AUTH_URL, {
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
audience: AUDIENCE,
}, {
headers: {
'Content-Type': 'application/json',
},
});
return response.data.access_token; // Extract the token
} catch (error) {
console.error('Error fetching access token:', error);
throw error;
}
};
// Function to fetch items from Content Hub API
export const getItems = async () => {
try {
const token = await getAccessToken(); // Get the access token
const response = await axios.get(`${CONTENT_HUB_API_URL}/items`, {
headers: {
Authorization: `Bearer ${token}`, // Use the token in Authorization header
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error('Error fetching items:', error);
throw error;
}
};
export const getItemById = async (id) => {
try {
const token = await getAccessToken(); // Get the access token
// Make the API request to fetch the item by its ID
const response = await axios.get(`${CONTENT_HUB_API_URL}/items/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
console.error(`Error fetching item with ID ${id}:`, error);
throw error;
}
};
Display Items in a React Component
Now that you’ve set up the functions to get the access token and fetch items, you can use these functions in a React component to display the list of items from Content Hub.
Create the Item List Component
This component will list all the items and allow users to click on an item to view its details.
// src/ItemsList.js
import React from 'react';
import { Link } from 'react-router-dom'; // Use Link for navigation
const ItemsList = ({ items }) => {
return (
<div style={{width:'500px'}}>
<h1>Content Hub ONE Items</h1>
<ul className="custom-list">
{items.map((item) => (
<li key={item.id}>
{/* Use Link to navigate to the details page */}
<Link to={`/item/${item.id}`}>
{item.name}
</Link>
</li>
))}
</ul>
</div>
);
};
export default ItemsList;
Create the Item Details Component
This component will fetch and display the details of the selected item using the ID from the URL.
// src/ItemDetail.js
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { getItemById } from './contentHubService'; // Import the service function
const ItemDetail = () => {
const { id } = useParams(); // Get the item ID from the URL params
const [item, setItem] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchItem = async () => {
try {
const data = await getItemById(id); // Fetch item by ID
setItem(data);
console.log(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchItem();
}, [id]); // Re-run the effect if the ID changes
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error loading item: {error.message}</div>;
}
if (!item) {
return <div>No item found</div>;
}
return (
<div>
<h1>{item.name}</h1>
<p>{item.description}</p>
{/* Render other item details here */}
</div>
);
};
export default ItemDetail;
Set Up Routing in App.js
Now, set up the routes in your App.js
file so you can navigate between the list and the details pages.
// src/App.js
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import ItemsList from './ItemsList';
import ItemDetail from './ItemDetail';
import { getItems } from './contentHubService'; // Import your service to get items
const App = () => {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchItems = async () => {
try {
const items = await getItems(); // Fetch all items
setItems(items.data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchItems();
}, []);
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error loading items: {error.message}</div>;
}
return (
<Router>
<Routes>
{/* Route to the list of items */}
<Route path="/" element={<ItemsList items={items} />} />
{/* Route to the item details page */}
<Route path="/item/:id" element={<ItemDetail />} />
</Routes>
</Router>
);
};
export default App;
CSS for List and Detail Pages:
You can apply some basic styling to differentiate the list and detail pages.
/* src/ItemsList.css */
ul.custom-list {
list-style-type: none;
padding: 0;
}
ul.custom-list li {
padding: 10px;
border-bottom: 1px solid #ddd;
}
ul.custom-list li a {
text-decoration: none;
color: #007bff;
font-weight: bold;
}
ul.custom-list li a:hover {
color: #0056b3;
}
/* src/ItemDetail.css */
div {
padding: 20px;
background-color: #f9f9f9;
border: 1px solid #ddd;
}
h1 {
font-size: 2em;
}
p {
font-size: 1.2em;
color: #333;
}
Run the Application
Make sure to start your app — npm start after setting everything up. You should see the output like this —
Item Details Page: When you click an item in the list, you are navigated to the details page, which uses the id
in the URL to fetch and display the details of that specific item.
In this blog, we have successfully integrated Sitecore Content Hub One Item APIs into a React app using OAuth2 authentication with client credentials. We covered the process of fetching an access token, using it to make authenticated requests, and displaying the content in the app.
By following these steps, you can create a seamless integration between your React application and Sitecore’s Content Hub One, empowering your app with rich content capabilities.
You can find my other blogs here.
Thank you!