Bygg en React-app som använder AEM GraphQL API:er
I det här kapitlet tittar du närmare på hur AEM GraphQL API:er kan styra upplevelsen i ett externt program.
En enkel React-app används för att fråga efter och visa Team- och Person-innehåll som exponeras av AEM GraphQL API:er. Användningen av React är i stort sett oviktig, och den uppladdande externa applikationen kan skrivas i vilket ramverk som helst för vilken plattform som helst.
Förutsättningar
Stegen som beskrivs i de tidigare delarna av den här flerdelade självstudiekursen har slutförts, eller så har basic-tutorial-solution.content.zip installerats på dina AEM Author- och Publish-tjänster.
Skärmbilder i IDE i det här kapitlet kommer från Visual Studio-kod
AEM
För att slutföra den här självstudiekursen rekommenderar vi att du har AEM Administrator-åtkomst till något av följande:
- AEM as a Cloud Service
- Lokal konfiguration med tjänsten AEM Cloud SDK
- AEM 6.5 LTS med GraphQL Index Package 1.0.5+ installerat
Programvarukrav
Följande programvara måste vara installerad:
- Node.js v18+
- Visual Studio-kod
- Git
- Java JDK (vid anslutning till lokal AEM SDK eller 6.5-instans)
Mål
Lär dig mer om:
- Hämta och starta exempelappen React
- Fråga AEM GraphQL slutpunkter med AEM Headless JS SDK
- Fråga AEM efter en lista över team och deras refererade medlemmar
- Fråga AEM efter information om en teammedlem
Hämta exempelappen React
I det här kapitlet implementeras en utbäddad exempelapp React med den kod som krävs för att interagera med AEM GraphQL API, och visa team- och persondata som hämtats från dem.
Källkoden för exempelappen React finns på Github.com på https://github.com/adobe/aem-guides-wknd-graphql/tree/main/basic-tutorial
Så här skaffar du React-appen:
-
Klona exempelappen WKND GraphQL React från Github.com. Kom ihåg att den här Git-databasen innehåller flera projekt, så glöm inte att navigera till mappen
basic-tutorialenligt beskrivningen i nästa steg.code language-shell $ cd ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git -
Navigera till mappen
basic-tutorialoch öppna den i din IDE.code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ code .
-
Uppdatera
.env.developmentför att ansluta till din AEM Publish-tjänst.Mer information om exempelprojektets miljövariabler och hur du ställer in dem finns i README.md.
AEM as a Cloud Service
När du ansluter React-appen till AEM as a Cloud Service Publish-tjänsten anger du värdet för
REACT_APP_HOST_URIsom din AEM as a Cloud Service Publish URL (t.ex.REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com) ochREACT_APP_AUTH_METHODhar värdetnoneAEM SDK för AEM as a Cloud Service (lokal)
När du ansluter React-appen till en lokal AEM as a Cloud Service SDK-miljö anger du värdet för
REACT_APP_HOST_URIsom din lokala värd och publiceringsport (till exempel.REACT_APP_HOST_URI=http://localhost:4503) ochREACT_APP_AUTH_METHODhar värdetnoneAEM 6.5 LTS-värdmiljö
När du ansluter React-appen till AEM as a Cloud Service Publish-tjänsten anger du värdet för
REACT_APP_HOST_URItill din publicerings-URL för AEM 6.5 (t.ex.REACT_APP_HOST_URI=https://dev.mysite.com) ochREACT_APP_AUTH_METHODhar värdetnoneAEM 6.5 LTS snabbstart (lokal)
När du ansluter React-appen till en lokal AEM 6.5-snabbstart anger du värdet för
REACT_APP_HOST_URIsom din lokala värd och publiceringsport (till exempel.REACT_APP_HOST_URI=http://localhost:4503) ochREACT_APP_AUTH_METHODhar värdetnonenote note NOTE Kontrollera att du har publicerat projektkonfigurationen, Content Fragment-modeller, redigerade innehållsfragment, GraphQL slutpunkter och beständiga frågor från tidigare steg. Om du utförde ovanstående steg på AEM Author SDK kan du peka på http://localhost:4502ochREACT_APP_AUTH_METHOD:s värde tillbasic. -
Gå till mappen
aem-guides-wknd-graphql/basic-tutorialfrån kommandoraden -
Starta React-appen
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ npm install $ npm start -
Appen React startar i utvecklingsläge på http://localhost:3000/. Ändringar som görs i React-appen under hela kursen återspeglas direkt.
Anatomi i React-appen
Exempelappen React består av tre huvuddelar:
-
Mappen
src/apiinnehåller filer som används för att skapa GraphQL-frågor till AEM.src/api/aemHeadlessClient.jsinitierar och exporterar AEM Headless Client som används för att kommunicera med AEMsrc/api/usePersistedQueries.jsimplementerar anpassade React-kopplingar som returnerar data från AEM GraphQL till vykomponenternaTeams.jsochPerson.js.
-
Filen
src/components/Teams.jsvisar en lista med team och deras medlemmar med hjälp av en listfråga. -
Filen
src/components/Person.jsvisar information om en person med hjälp av en parametriserad fråga med ett resultat.
Granska objektet AEMHeadless
Granska filen aemHeadlessClient.js för hur du skapar det AEMHeadless-objekt som används för att kommunicera med AEM.
-
Öppna
src/api/aemHeadlessClient.js. -
Granska raderna 1-40:
-
Importdeklarationen
AEMHeadlessfrån AEM Headless Client för JavaScript, rad 11. -
Auktoriseringskonfigurationen baseras på variabler som definierats i
.env.development, rad 14-22 och, pilfunktionsuttrycketsetAuthorization, rad 31-40. -
serviceUrl-konfigurationen för den inkluderade utvecklingsproxykonfigurationen, rad 27.
-
-
Raderna 42-49 är viktigast eftersom de instansierar klienten
AEMHeadlessoch exporterar den för användning i hela React-appen.
// Initialize the AEM Headless Client and export it for other files to use
const aemHeadlessClient = new AEMHeadless({
serviceURL: serviceURL,
endpoint: REACT_APP_GRAPHQL_ENDPOINT,
auth: setAuthorization(),
});
export default aemHeadlessClient;
Implementera för att köra beständiga AEM GraphQL-frågor
Om du vill implementera den generiska funktionen fetchPersistedQuery(..) för att köra AEM GraphQL beständiga frågor öppnar du filen usePersistedQueries.js. Funktionen fetchPersistedQuery(..) använder aemHeadlessClient-objektets runPersistedQuery()-funktion för att köra frågan asynkront, löftesbaserat beteende.
Senare anropar den anpassade funktionen React useEffect den för att hämta specifika data från AEM.
- I
src/api/usePersistedQueries.jsupdatefetchPersistedQuery(..), rad 35, med koden nedan.
/**
* Private, shared function that invokes the AEM Headless client.
*
* @param {String} persistedQueryName the fully qualified name of the persisted query
* @param {*} queryParameters an optional JavaScript object containing query parameters
* @returns the GraphQL data or an error message
*/
async function fetchPersistedQuery(persistedQueryName, queryParameters) {
let data;
let err;
try {
// AEM GraphQL queries are asynchronous, either await their return or use Promise-based syntax
const response = await aemHeadlessClient.runPersistedQuery(
persistedQueryName,
queryParameters
);
// The GraphQL data is stored on the response's data field
data = response?.data;
} catch (e) {
// An error occurred, return the error messages
err = e
.toJSON()
?.map((error) => error.message)
?.join(", ");
console.error(e.toJSON());
}
// Return the GraphQL and any errors
return { data, err };
}
Implementera Teams-funktioner
Bygg sedan upp funktionaliteten för att visa teamen och deras medlemmar i React-appens huvudvy. Den här funktionen kräver:
- En ny anpassad React useEffect-krok i
src/api/usePersistedQueries.jssom anropar denmy-project/all-teamsbeständiga frågan och returnerar en lista med Team Content Fragments i AEM. - En React-komponent på
src/components/Teams.jssom anropar den nya anpassade ReactuseEffect-kroken och återger teamdata.
När det är klart fylls programmets huvudvy i med teamdata från AEM.
Steg
-
Öppna
src/api/usePersistedQueries.js. -
Hitta funktionen
useAllTeams() -
Om du vill skapa en
useEffect-krok som anropar den beständiga fråganmy-project/all-teamsviafetchPersistedQuery(..)lägger du till följande kod. Haken returnerar bara relevanta data från AEM GraphQL-svaret pådata?.teamList?.items, vilket gör att komponenterna i vyn React kan vara agnostiska för de överordnade JSON-strukturerna.code language-javascript /** * Custom hook that calls the 'my-project/all-teams' persisted query. * * @returns an array of Team JSON objects, and array of errors */ export function useAllTeams() { const [teams, setTeams] = useState(null); const [error, setError] = useState(null); // Use React useEffect to manage state changes useEffect(() => { async function fetchData() { // Call the AEM GraphQL persisted query named "my-project/all-teams" const { data, err } = await fetchPersistedQuery( "my-project/all-teams" ); // Sets the teams variable to the list of team JSON objects setTeams(data?.teamList?.items); // Set any errors setError(err); } // Call the internal fetchData() as per React best practices fetchData(); }, []); // Returns the teams and errors return { teams, error }; } -
Öppna
src/components/Teams.js -
Hämta listan med team från AEM med kroken
Teamsi komponentenuseAllTeams()React.code language-javascript import { useAllTeams } from "../api/usePersistedQueries"; ... function Teams() { // Get the Teams data from AEM using the useAllTeams const { teams, error } = useAllTeams(); ... } -
Utför den vybaserade dataverifieringen och visa ett felmeddelande eller en inläsningsindikator baserat på returnerade data.
code language-javascript function Teams() { const { teams, error } = useAllTeams(); // Handle error and loading conditions if (error) { // If an error ocurred while executing the GraphQL query, display an error message return <Error errorMessage={error} />; } else if (!teams) { // While the GraphQL request is executing, show the Loading indicator return <Loading />; } ... } -
Återge slutligen teamdata. Varje team som returneras från GraphQL-frågan återges med den tillhandahållna
TeamReact-underkomponenten.code language-javascript import React from "react"; import { Link } from "react-router-dom"; import { useAllTeams } from "../api/usePersistedQueries"; import Error from "./Error"; import Loading from "./Loading"; import "./Teams.scss"; function Teams() { const { teams, error } = useAllTeams(); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!teams) { return <Loading />; } // Teams have been populated by AEM GraphQL query. Display the teams. return ( <div className="teams"> {teams.map((team, index) => { return <Team key={index} {...team} />; })} </div> ); } // Render single Team function Team({ title, shortName, description, teamMembers }) { // Must have title, shortName and at least 1 team member if (!title || !shortName || !teamMembers) { return null; } return ( <div className="team"> <h2 className="team__title">{title}</h2> <p className="team__description">{description.plaintext}</p> <div> <h4 className="team__members-title">Members</h4> <ul className="team__members"> {/* Render the referenced Person models associated with the team */} {teamMembers.map((teamMember, index) => { return ( <li key={index} className="team__member"> <Link to={`/person/${teamMember.fullName}`}> {teamMember.fullName} </Link> </li> ); })} </ul> </div> </div> ); } export default Teams;
Implementera personfunktion
Med funktionen Teams slutförd kan vi implementera funktionen för att hantera visningen av en gruppmedlems, eller en persons, information.
Den här funktionen kräver:
-
En ny anpassad React useEffect-krok i
src/api/usePersistedQueries.jssom anropar den parametriserademy-project/person-by-namebeständiga frågan och returnerar en person. -
En React-komponent på
src/components/Person.jssom använder en persons fullständiga namn som frågeparameter, anropar den nya anpassade ReactuseEffect-kroken och återger persondata.
När du är klar återges en personvy när du väljer en persons namn i Teams-vyn.
-
Öppna
src/api/usePersistedQueries.js. -
Hitta funktionen
usePersonByName(fullName) -
Om du vill skapa en
useEffect-krok som anropar den beständiga fråganmy-project/all-teamsviafetchPersistedQuery(..)lägger du till följande kod. Haken returnerar bara relevanta data från AEM GraphQL-svaret pådata?.teamList?.items, vilket gör att komponenterna i vyn React kan vara agnostiska för de överordnade JSON-strukturerna.code language-javascript /** * Calls the 'my-project/person-by-name' and provided the {fullName} as the persisted query's `name` parameter. * * @param {String!} fullName the full * @returns a JSON object representing the person */ export function usePersonByName(fullName) { const [person, setPerson] = useState(null); const [errors, setErrors] = useState(null); useEffect(() => { async function fetchData() { // The key is the variable name as defined in the persisted query, and may not match the model's field name const queryParameters = { name: fullName }; // Invoke the persisted query, and pass in the queryParameters object as the 2nd parameter const { data, err } = await fetchPersistedQuery( "my-project/person-by-name", queryParameters ); if (err) { // Capture errors from the HTTP request setErrors(err); } else if (data?.personList?.items?.length === 1) { // Set the person data after data validation setPerson(data.personList.items[0]); } else { // Set an error if no person could be found setErrors(`Cannot find person with name: ${fullName}`); } } fetchData(); }, [fullName]); return { person, errors }; } -
Öppna
src/components/Person.js -
Analysera vägparametern
Personi komponentenfullNameoch hämta persondata från AEM med hjälp av krokenusePersonByName(fullName).code language-javascript import { useParams } from "react-router-dom"; import { usePersonByName } from "../api/usePersistedQueries"; ... function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); ... } -
Utför vybaserad dataverifiering och visa ett felmeddelande eller en inläsningsindikator baserat på returnerade data.
code language-javascript function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!person) { return <Loading />; } ... } -
Återge sedan persondata.
code language-javascript import React from "react"; import { useParams } from "react-router-dom"; import { usePersonByName } from "../api/usePersistedQueries"; import { mapJsonRichText } from "../utils/renderRichText"; import Error from "./Error"; import Loading from "./Loading"; import "./Person.scss"; function Person() { // Read the person's `fullName` which is the parameter used to query for the person's details const { fullName } = useParams(); // Query AEM for the Person's details, using the `fullName` as the filtering parameter const { person, error } = usePersonByName(fullName); // Handle error and loading conditions if (error) { return <Error errorMessage={error} />; } else if (!person) { return <Loading />; } // Render the person data return ( <div className="person"> <img className="person__image" src={process.env.REACT_APP_HOST_URI+person.profilePicture._path} alt={person.fullName} /> <div className="person__occupations"> {person.occupation.map((occupation, index) => { return ( <span key={index} className="person__occupation"> {occupation} </span> ); })} </div> <div className="person__content"> <h1 className="person__full-name">{person.fullName}</h1> <div className="person__biography"> {/* Use this utility to transform multi-line text JSON into HTML */} {mapJsonRichText(person.biographyText.json)} </div> </div> </div> ); } export default Person;
CORS-konfiguration
Det här exemplet på React-appen kräver inte resursdelning över ursprung (CORS) under utvecklingen eftersom den ansluter till AEM med en lokal proxy. Lokal proxy används endast för att underlätta snabb utveckling och inte för icke-utvecklingsbruk.
I produktionsscenarier är det dock oftast bäst att klientprogrammet kommunicerar direkt med AEM via användarens webbläsare. Om du vill aktivera detta kan CORS behöva konfigureras i AEM för att tillåta hämtningsbegäranden från ditt webbprogram.
Prova appen
Granska appen http://localhost:3000/ och klicka på länkarna Medlemmar. Du kan också lägga till fler team och/eller medlemmar i Team Alpha genom att lägga till innehållsfragment i AEM.
solution grenen.Under hålet
Öppna webbläsarens utvecklingsverktyg > Nätverk och Filter för all-teams-begäran. Observera att GraphQL API-begäran /graphql/execute.json/my-project/all-teams görs mot http://localhost:3000 och NOT mot värdet för REACT_APP_HOST_URI, till exempel <https://publish-pxxx-exxx.adobeaemcloud.com. Begärandena görs mot React-appens domän eftersom proxykonfigurationen har aktiverats med modulen http-proxy-middleware.
Granska huvudfilen ../setupProxy.js och inom ../proxy/setupProxy.auth.**.js filer observerar du hur /content- och /graphql-sökvägarna proxideras och anger att det inte är en statisk resurs.
module.exports = function(app) {
app.use(
['/content', '/graphql'],
...
Att använda den lokala proxyn är inte ett lämpligt alternativ för produktionsdistribution och mer information finns i avsnittet Produktionsdistribution.
Grattis! congratulations
Grattis! Du har nu skapat React-appen för att använda och visa data från AEM GraphQL API:er som en del av den grundläggande självstudiekursen!