建立使用AEM GraphQL API的React應用程式
在本章中,您將探索AEM的GraphQL API如何推動外部應用程式中的體驗。
使用簡單的React應用程式來查詢及顯示AEM的GraphQL API所公開的 團隊 和 人員 內容。 React的使用在很大程度上不重要,而且消費的外部應用程式可以寫入任何平台的任何框架中。
先決條件
我們已假設完成此多部分教學課程前幾部分所概述的步驟,或已在您的AEM as a Cloud Service作者和Publish服務上安裝basic-tutorial-solution.content.zip。
本章中的 IDE熒幕擷取畫面來自Visual Studio Code
必須安裝下列軟體:
目標
瞭解如何:
- 下載並啟動範例React應用程式
- 使用AEM Headless JS SDK查詢AEM的GraphQL端點
- 查詢AEM以取得團隊及其參照成員的清單
- 查詢AEM以取得團隊成員的詳細資訊
取得範例React應用程式
在本章中,使用與AEM的GraphQL API互動所需的程式碼實作一個草稿範例React應用程式,並顯示從這些API取得的團隊和人員資料。
範例React應用程式原始碼可在Github.com上的https://github.com/adobe/aem-guides-wknd-graphql/tree/main/basic-tutorial取得
若要取得React應用程式:
-
從Github.com複製範例WKND GraphQL React應用程式。
code language-shell $ cd ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
導覽至
basic-tutorial
資料夾,並在IDE中開啟。code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ code .
在VSCode中
-
更新
.env.development
以連線至AEM as a Cloud Service Publish服務。- 將
REACT_APP_HOST_URI
的值設為您的AEM as a Cloud Service Publish URL (例如REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
)和REACT_APP_AUTH_METHOD
的值至none
note note NOTE 請確定您已發佈專案設定、內容片段模型、編寫的內容片段、GraphQL端點,以及先前步驟中的持續查詢。 如果您在本機AEM Author SDK上執行上述步驟,您可以指向 http://localhost:4502
並將REACT_APP_AUTH_METHOD
的值指向basic
。 - 將
-
從命令列,前往
aem-guides-wknd-graphql/basic-tutorial
資料夾 -
啟動React應用程式
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ npm install $ npm start
-
React應用程式會在http://localhost:3000/上以開發模式啟動。 在教學課程中對React應用程式所做的變更會立即反映出來。
React應用程式剖析
範例React應用程式有三個主要部分:
-
src/api
資料夾包含用來向AEM發出GraphQL查詢的檔案。src/api/aemHeadlessClient.js
會初始化並匯出用來與AEM通訊的AEM Headless使用者端src/api/usePersistedQueries.js
實作自訂React鉤點從AEM GraphQL傳回資料到Teams.js
和Person.js
檢視元件。
-
src/components/Teams.js
檔案使用清單查詢來顯示團隊及其成員的清單。 -
src/components/Person.js
檔案使用引數化的單一結果查詢,顯示單一人員的詳細資料。
檢閱AEMHeadless物件
檢閱aemHeadlessClient.js
檔案,瞭解如何建立用來與AEM通訊的AEMHeadless
物件。
-
開啟
src/api/aemHeadlessClient.js
。 -
複查明細行1-40:
-
從AEM Headless Client for JavaScript匯入
AEMHeadless
宣告,第11行。 -
根據
.env.development
、第14-22行中定義的變數以及箭號函式運算式setAuthorization
、第31-40行所定義的授權組態。 -
內含開發Proxy組態的
serviceUrl
設定,第27行。
-
-
第42到49行是最重要的,因為它們會將
AEMHeadless
使用者端例項化,並將其匯出以供在整個React應用程式中使用。
// 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;
實作以執行AEM GraphQL持續查詢
若要實作泛型fetchPersistedQuery(..)
函式以執行AEM GraphQL持續查詢,請開啟usePersistedQueries.js
檔案。 fetchPersistedQuery(..)
函式使用aemHeadlessClient
物件的runPersistedQuery()
函式以非同步、承諾型行為執行查詢。
稍後,自訂React useEffect
連結呼叫此函式以從AEM擷取特定資料。
- 在
src/api/usePersistedQueries.js
中 更新fetchPersistedQuery(..)
,第35行,代碼如下。
/**
* 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 };
}
實作團隊功能
接下來,建置可在React應用程式的主要檢視上顯示團隊及其成員的功能。 此功能需要:
- 在
src/api/usePersistedQueries.js
中的新自訂React useEffect勾點,它會叫用my-project/all-teams
持續查詢,並傳回AEM中的Team內容片段清單。 - 位於
src/components/Teams.js
的React元件會叫用新的自訂ReactuseEffect
鉤點,並轉譯Teams資料。
完成後,應用程式的主要檢視會填入來自AEM的團隊資料。
步驟
-
開啟
src/api/usePersistedQueries.js
。 -
找到函式
useAllTeams()
-
若要建立透過
fetchPersistedQuery(..)
叫用持續查詢my-project/all-teams
的useEffect
連結,請新增下列程式碼。 此掛接也只會從data?.teamList?.items
的AEM GraphQL回應傳回相關資料,讓React檢視元件與父JSON結構無關。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 }; }
-
開啟
src/components/Teams.js
-
在
Teams
React元件中,使用useAllTeams()
勾點從AEM擷取團隊清單。code language-javascript import { useAllTeams } from "../api/usePersistedQueries"; ... function Teams() { // Get the Teams data from AEM using the useAllTeams const { teams, error } = useAllTeams(); ... }
-
執行以檢視為基礎的資料驗證,根據傳回的資料顯示錯誤訊息或載入指標。
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 />; } ... }
-
最後,轉譯團隊資料。 從GraphQL查詢傳回的每個團隊都會使用提供的
Team
React子元件轉譯。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;
實作人員功能
完成團隊功能後,讓我們實作這項功能以處理團隊成員或個人詳細資訊上的顯示。
此功能需要:
-
src/api/usePersistedQueries.js
中的新自訂React useEffect勾點,它會叫用引數化my-project/person-by-name
持續查詢,並傳回單一人員記錄。 -
位於
src/components/Person.js
的React元件使用個人的全名作為查詢引數,叫用新的自訂ReactuseEffect
連結,並呈現個人資料。
完成後,在「團隊」檢視中選取人員名稱,即可呈現人員檢視。
-
開啟
src/api/usePersistedQueries.js
。 -
找到函式
usePersonByName(fullName)
-
若要建立透過
fetchPersistedQuery(..)
叫用持續查詢my-project/all-teams
的useEffect
連結,請新增下列程式碼。 此掛接也只會從data?.teamList?.items
的AEM GraphQL回應傳回相關資料,讓React檢視元件與父JSON結構無關。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 }; }
-
開啟
src/components/Person.js
-
在
Person
React元件中,剖析fullName
路由引數,並使用usePersonByName(fullName)
勾點從AEM擷取人員資料。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); ... }
-
執行以檢視為基礎的資料驗證,根據傳回的資料顯示錯誤訊息或載入指標。
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 />; } ... }
-
最後,呈現人員資料。
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;
試用應用程式
檢閱應用程式http://localhost:3000/並按一下 成員 連結。 您也可以在AEM中新增內容片段,以新增更多團隊和/或成員至團隊Alpha。
潛藏在引擎蓋下
開啟瀏覽器的 開發人員工具 > 網路 和 篩選器,以處理all-teams
請求。 請注意,GraphQL API要求/graphql/execute.json/my-project/all-teams
是針對http://localhost:3000
而提出的,且 NOT 是針對REACT_APP_HOST_URI
的值,例如<https://publish-pxxx-exxx.adobeaemcloud.com
。 這些要求是針對React應用程式的網域所發出,因為Proxy設定是使用http-proxy-middleware
模組來啟用。
透過Proxy的
檢閱主要../setupProxy.js
檔案,並在../proxy/setupProxy.auth.**.js
個檔案中注意到/content
和/graphql
路徑是如何進行代理的,並表明它不是靜態資產。
module.exports = function(app) {
app.use(
['/content', '/graphql'],
...
使用本機Proxy不適合用於生產部署,您可以在 生產部署 區段找到更多詳細資料。
恭喜! congratulations
恭喜!您已成功建立React應用程式,以使用並顯示AEM的GraphQL API資料,作為基本教學課程的一部分!