Criar um aplicativo React que use APIs GraphQL do AEM
Neste capítulo, você explora como as APIs do GraphQL do AEM podem impulsionar a experiência em um aplicativo externo.
Um aplicativo React simples é usado para consultar e exibir conteúdo de Equipe e Pessoa exposto por APIs GraphQL do AEM. O uso do React não é muito importante, e o aplicativo externo de consumo poderia ser escrito em qualquer estrutura para qualquer plataforma.
Pré-requisitos
Pressupõe-se que as etapas descritas nas partes anteriores deste tutorial de várias partes tenham sido concluídas ou que o basic-tutorial-solution.content.zip esteja instalado nos serviços do AEM as a Cloud Service Author e da Publish.
As capturas de tela do IDE neste capítulo vêm do Visual Studio Code
Os softwares a seguir devem ser instalados:
Objetivos
Saiba como:
- Baixe e inicie o aplicativo React de exemplo
- Consultar pontos de extremidade do GraphQL do AEM usando o SDK JS do AEM Headless
- Consultar AEM para obter uma lista de equipes e seus membros referenciados
- Consultar AEM para obter detalhes de um membro da equipe
Obter a amostra do aplicativo React
Neste capítulo, um aplicativo React de amostra reprovada é implementado com o código necessário para interagir com a API GraphQL do AEM e exibir os dados de equipe e pessoa obtidos deles.
O exemplo de código-fonte do aplicativo React está disponível em Github.com em https://github.com/adobe/aem-guides-wknd-graphql/tree/main/basic-tutorial
Para obter o aplicativo React:
-
Clonar o aplicativo WKND GraphQL React de exemplo de Github.com.
code language-shell $ cd ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
Navegue até a pasta
basic-tutorial
e abra-a no IDE.code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ code .
-
Atualize
.env.development
para se conectar ao serviço AEM as a Cloud Service Publish.- Defina o valor de
REACT_APP_HOST_URI
como a URL do Publish do AEM as a Cloud Service (por exemplo,REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
) e o valor deREACT_APP_AUTH_METHOD
paranone
note note NOTE Verifique se você publicou a configuração do projeto, os modelos de fragmento de conteúdo, os fragmentos de conteúdo criados, os endpoints do GraphQL e as consultas persistentes das etapas anteriores. Se você executou as etapas acima no SDK do autor do AEM local, é possível apontar para http://localhost:4502
e o valor deREACT_APP_AUTH_METHOD
parabasic
. - Defina o valor de
-
Na linha de comando, vá para a pasta
aem-guides-wknd-graphql/basic-tutorial
-
Iniciar o aplicativo React
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial $ npm install $ npm start
-
O aplicativo React é iniciado no modo de desenvolvimento em http://localhost:3000/. As alterações feitas no aplicativo React em todo o tutorial são refletidas imediatamente.
Anatomia do aplicativo React
A amostra do aplicativo React tem três partes principais:
-
A pasta
src/api
contém arquivos usados para fazer consultas GraphQL ao AEM.src/api/aemHeadlessClient.js
inicializa e exporta o cliente AEM Headless usado para se comunicar com AEMsrc/api/usePersistedQueries.js
implementa ganchos de Reação personalizados para retornar dados do AEM GraphQL para os componentes de exibiçãoTeams.js
ePerson.js
.
-
O arquivo
src/components/Teams.js
exibe uma lista de equipes e seus membros, usando uma consulta de lista. -
O arquivo
src/components/Person.js
exibe os detalhes de uma única pessoa, usando uma consulta parametrizada de resultado único.
Revisar o objeto AEMHeadless
Revise o arquivo aemHeadlessClient.js
para saber como criar o objeto AEMHeadless
usado para se comunicar com AEM.
-
Abrir
src/api/aemHeadlessClient.js
. -
Revise as linhas 1-40:
-
A declaração de importação
AEMHeadless
do Cliente AEM Headless para JavaScript, linha 11. -
A configuração da autorização com base nas variáveis definidas em
.env.development
, linha 14-22, e, a expressão de função de setasetAuthorization
, linha 31-40. -
A configuração
serviceUrl
da configuração de proxy de desenvolvimento incluída, linha 27.
-
-
As linhas 42 a 49 são mais importantes, pois instanciam o cliente
AEMHeadless
e o exportam para uso em todo o aplicativo 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;
Implementar para executar consultas persistentes do AEM GraphQL
Para implementar a função fetchPersistedQuery(..)
genérica para executar as consultas persistentes do AEM GraphQL, abra o arquivo usePersistedQueries.js
. A função fetchPersistedQuery(..)
usa a função runPersistedQuery()
do objeto aemHeadlessClient
para executar a consulta de forma assíncrona, com base em promessa.
Mais tarde, o gancho useEffect
personalizado do React chama essa função para recuperar dados específicos do AEM.
- Em
src/api/usePersistedQueries.js
atualizarfetchPersistedQuery(..)
, linha 35, com o código abaixo.
/**
* 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 };
}
Implementar a funcionalidade Equipes
Em seguida, crie a funcionalidade para exibir as Equipes e seus membros na visualização principal do aplicativo React. Essa funcionalidade exige:
- Um novo gancho useEffect personalizado do React em
src/api/usePersistedQueries.js
que invoca a consulta persistentemy-project/all-teams
, retornando uma lista de Fragmentos de Conteúdo de Equipe no AEM. - Um componente React em
src/components/Teams.js
que invoca o novo gancho ReactuseEffect
personalizado e renderiza os dados das equipes.
Uma vez concluída, a visualização principal do aplicativo é preenchida com os dados das equipes do AEM.
Etapas
-
Abrir
src/api/usePersistedQueries.js
. -
Localizar a função
useAllTeams()
-
Para criar um gancho
useEffect
que chame a consulta persistentemy-project/all-teams
viafetchPersistedQuery(..)
, adicione o seguinte código. O gancho também retorna apenas os dados relevantes da resposta do AEM GraphQL emdata?.teamList?.items
, permitindo que os componentes da exibição do React sejam agnósticos das estruturas JSON principais.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 }; }
-
Abrir
src/components/Teams.js
-
No componente React
Teams
, busque a lista de equipes do AEM usando o ganchouseAllTeams()
.code language-javascript import { useAllTeams } from "../api/usePersistedQueries"; ... function Teams() { // Get the Teams data from AEM using the useAllTeams const { teams, error } = useAllTeams(); ... }
-
Execute a validação de dados com base em visualização, exibindo uma mensagem de erro ou indicador de carregamento com base nos dados retornados.
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 />; } ... }
-
Por fim, renderize os dados das equipes. Cada equipe retornada da consulta do GraphQL é renderizada usando o subcomponente React
Team
fornecido.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;
Implementar a funcionalidade Pessoa
Com a funcionalidade Equipes concluída, vamos implementar a funcionalidade para lidar com a exibição nos detalhes de um membro da equipe ou pessoa.
Essa funcionalidade exige:
-
Um novo gancho useEffect personalizado do React em
src/api/usePersistedQueries.js
que invoca a consulta persistentemy-project/person-by-name
parametrizada e retorna um único registro de pessoa. -
Um componente do React em
src/components/Person.js
que usa o nome completo de uma pessoa como um parâmetro de consulta, invoca o novo gancho personalizadouseEffect
do React e renderiza os dados da pessoa.
Uma vez concluída, selecionar o nome de uma pessoa na exibição Equipes renderiza a exibição de pessoa.
-
Abrir
src/api/usePersistedQueries.js
. -
Localizar a função
usePersonByName(fullName)
-
Para criar um gancho
useEffect
que chame a consulta persistentemy-project/all-teams
viafetchPersistedQuery(..)
, adicione o seguinte código. O gancho também retorna apenas os dados relevantes da resposta do AEM GraphQL emdata?.teamList?.items
, permitindo que os componentes da exibição do React sejam agnósticos das estruturas JSON principais.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 }; }
-
Abrir
src/components/Person.js
-
No componente React
Person
, analise o parâmetro de rotafullName
e busque os dados da pessoa do AEM usando o ganchousePersonByName(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); ... }
-
Execute a validação de dados com base em visualização, exibindo uma mensagem de erro ou indicador de carregamento com base nos dados retornados.
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 />; } ... }
-
Por fim, renderize os dados da pessoa.
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;
Experimente o aplicativo
Revise o aplicativo http://localhost:3000/ e clique em links Membros. Você também pode adicionar mais equipes e/ou membros ao Alpha da equipe, adicionando Fragmentos de conteúdo no AEM.
Debaixo da tampa
Abra as Ferramentas do Desenvolvedor > Rede e Filtro do navegador para a solicitação all-teams
. Observe que a solicitação da API do GraphQL /graphql/execute.json/my-project/all-teams
é feita contra http://localhost:3000
e NÃO contra o valor de REACT_APP_HOST_URI
, por exemplo <https://publish-pxxx-exxx.adobeaemcloud.com
. As solicitações são feitas no domínio do aplicativo React porque a configuração de proxy está habilitada usando o módulo http-proxy-middleware
.
Revise o arquivo principal ../setupProxy.js
e, dentro de ../proxy/setupProxy.auth.**.js
arquivos, observe como /content
e /graphql
caminhos são encaminhados por proxy e indicaram que não é um ativo estático.
module.exports = function(app) {
app.use(
['/content', '/graphql'],
...
O uso do proxy local não é uma opção adequada para implantação de produção, e mais detalhes podem ser encontrados na seção Implantação de Produção.
Parabéns. congratulations
Parabéns! Você criou com sucesso o aplicativo React para consumir e exibir dados das APIs do GraphQL do AEM como parte de um tutorial básico!