Building a React App with AEM Integration using GraphQL

Kinjal P Darji
5 min readApr 5, 2024
Image Curtesy : https://experienceleague.adobe.com/

Here, I’ll be demonstrating how a simple react app can be created that users AEM GraphQL to show the blog posts.

First, we need to create a react app. Use below commands to do so. Reference — https://legacy.reactjs.org/docs/create-a-new-react-app.html

npx create-react-app new-react-app

cd new-react-app

npm start

The command “npm start” will open http://localhost:3000 and it will have default react screen.

Then, we need to update the react app code so that it makes a call to AEM GraphQL. But before we do that, we need to create a GraphQL endpoint in AEM. Steps for the same as listed below.

  1. Go to Tools > GrahpQL (Manage GraphQL Endpoints) and click on Create button.
  2. This will open a dialog where you need to provide the name (for example aemtechblog) and schema (/conf/global). Schema will be by default selected.

Once above is done, we need to create content fragments. I’ve created a new content fragment model called posts inside /conf/aemtechblog. This conf folder should have Content Fragment models enabled in order to create content fragment models.

The content fragment model looks like below. This model has four inputs — id, user id, title and body.

Now, we can create some content fragments as below. I’ve created these fragments inside /content/dam/aemtechblog. This folder should be orderable folder. I’ve taken the data from https://jsonplaceholder.typicode.com/posts.

Now, let’s test this with the inbuilt GraphQL query editor.

Go to Tools > GraphQL Query Editor and type below query

query allPosts {
postsList {
items {
id,
userId,
title,
body {
plaintext
},
}
}
}

Run the query and there should be results shown as below.

This can also be tested on the postman like below. Notice the body tab configured to use GraphQL.

Since the react app will be running on http://localhost:3000 and AEM will be running on http://localhost:4502. There’ll be cross site reference errors. And AEM will return 403 forbidden for all the queries done to AEM from React. To resolve this, csrf filter and referrer filter will need to stop filtering post method. And also http://localhost:3000 will have to be added as allowed origin. Also, in http://localhost:4502/system/console/configMgr, there’s a configuration for Adobe granite cross-origin resource sharing policy, which will need to include http://localhost:3000 as the allowed origins. (Phew!)

Now we’ll focus back on react app. I’ve used — https://medium.com/quick-code/build-simple-react-js-application-in-5-minutes-f9abfc2d018a to learn about creating new react app.

We need to fetch the results from AEM to React App and show the same on page. For this, I’m using basic fetch call. This can be improvised to use promise. Also, I’ve done it directly in App.js, which can be refactored as per react best practices.

Below is the code that I’ve used to fetch the results from AEM.

componentDidMount() {
fetch(
" http://localhost:4502/content/cq:graphql/aemtechblog/endpoint.json",
{
method: "POST",

headers: {
"Content-Type": "application/json",
Authorization : "Basic .... ",
},

body: JSON.stringify({
query: `query allPosts {
postsList {
items {
id,
userId,
title,
body {
plaintext
},
}
}
}
`,
}),
}
)
.then((res) => res.json())
.then((res) => {
console.log(res);
this.setState({ posts: res.data.postsList.items });
});
}

My complete App.js looks like below.

import React, { Component } from "react";
import "./App.css";
import base64 from "base-64";

class App extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
};
}

componentDidMount() {
fetch(
" http://localhost:4502/content/cq:graphql/aemtechblog/endpoint.json",
{
method: "POST",

headers: {
"Content-Type": "application/json",
Authorization : "Basic .... ",
},

body: JSON.stringify({
query: `query allPosts {
postsList {
items {
id,
userId,
title,
body {
plaintext
},
}
}
}
`,
}),
}
)
.then((res) => res.json())
.then((res) => {
console.log(res);
this.setState({ posts: res.data.postsList.items });
});
}

render() {
const { posts } = this.state;
console.log(posts);
return (
<div className="App">
<div class="jumbotron">
<h1 class="display-4">Blog posts</h1>
</div>
<div class="card-container">
{posts.map((post) => (
<div className="card" key={post.id}>
<div className="card-header">
#{post.id} {post.title}
</div>
<div className="card-body">
<p className="card-text">{post.body.plaintext}</p>
</div>
</div>
))}
</div>
</div>
);
}
}
export default App;

And below is my App.css for the better formatting of my page.

.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.App {
padding-left: 100px;
padding-right : 100px;
}

.App .jumbotron {
text-align: center;
}

.App .card {
margin-bottom: 10px;
}

.App .card-header {
color: white;
background-color : blue;
font-weight: bold;
}

.App .card-container {

display : grid;
grid-template-columns : auto auto auto auto;
justify-content: center;
align-items: stretch;
justify-items: center;
align-content: stretch;
grid-gap : 20px;
}

And that’s it. Accessing http://localhost:3000 will show below output on browser.

--

--

Kinjal P Darji

Hi, I am an AEM architect and a certified AWS Developer — Associate.