Cliente Apollo (React) - SDK da Web
Nesta página
GraphQL está obsoleto. Saiba mais.
Visão geral
Você pode usar o cliente Apollo para se conectar à GraphQL API exposta do seu aplicativo Realm a partir de um aplicação React. O Cliente Apollo executa consultas e mutações, mantém um cache de dados no lado do cliente e se integra ao seu aplicativo com componentes e ganchos React idiomáticos.
Observação
Veja um aplicativo de demonstração funcional
Confira o Realm GraphQL - Apollo (React) repositório no Github para ver um aplicativo React & Apollo totalmente configurado que está pronto para se conectar ao seu próprio Atlas App Services backend do . Ele usa a collection sample_mflix.movies
incluída nos conjuntos de dados de exemplo do MongoDB Atlas .
Se você não quiser clonar o repositório, o aplicativo de demonstração também estará disponível no navegador no Realm GraphQL CodeSandbox.
Configurar o cliente Apollo
Instalar dependências
Como em qualquer projeto do Realm, será necessário instalar o Realm Web SDK para autenticar usuários e solicitações.
npm install realm-web
A Apollo agrupa os componentes centrais necessários para criar um cliente em um pacote chamado @apollo/cliente. Também requer o pacote graphql para analisar queries GraphQL.
npm install @apollo/client graphql
Criar um cliente Apollo GraphQL
Crie um novo objeto ApolloClient que aponte para o ponto de extremidade da API GraphQL do seu aplicativo Realm. Você gera a URL do ponto com base na ID do aplicativo do Realm ou a encontra na página GraphQL da IU do Realm.
// Add your App ID const graphqlUri = `https://services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql`; // Local apps should use a local URI! // const graphqlUri = `https://us-east-1.aws.services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql` // const graphqlUri = `https://eu-west-1.aws.services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql` // const graphqlUri = `https://ap-southeast-1.aws.services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql` const client = new ApolloClient({ link: new HttpLink({ uri: graphqlUri, }), cache: new InMemoryCache(), });
Configurar autenticação de usuário
O ApolloClient
está configurado para enviar solicitações ao seu aplicativo. No entanto, todas as solicitações do Realm GraphQL devem incluir um token de acesso de usuário válido para autenticar as solicitações. Portanto, no momento, qualquer operação enviada pelo Apollo falhará. Os tokens de acesso expiram após 30 minutos e precisam ser atualizados.
Para autenticar solicitações, você precisa adicionar um cabeçalho de autorização com um token de acesso de usuário do Realm válido a cada solicitação do GraphQ.
Você pode autenticar um usuário e obter seu token de acesso com o Realm Web SDK. O objeto do Apollo HttpLink
permite adicionar cabeçalhos personalizados a cada solicitação ao definir uma função de fetch
personalizada.
// Connect to your MongoDB Realm app const app = new Realm.App(APP_ID); // Gets a valid Realm user access token to authenticate requests async function getValidAccessToken() { // Guarantee that there's a logged in user with a valid access token if (!app.currentUser) { // If no user is logged in, log in an anonymous user. The logged in user will have a valid // access token. await app.logIn(Realm.Credentials.anonymous()); } else { // An already logged in user's access token might be stale. Tokens must be refreshed after // 30 minutes. To guarantee that the token is valid, we refresh the user's access token. await app.currentUser.refreshAccessToken(); } return app.currentUser.accessToken; } // Configure the ApolloClient to connect to your app's GraphQL endpoint const client = new ApolloClient({ link: new HttpLink({ uri: `https://services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql`, // We define a custom fetch handler for the Apollo client that lets us authenticate GraphQL requests. // The function intercepts every Apollo HTTP request and adds an Authorization header with a valid // access token before sending the request. fetch: async (uri, options) => { const accessToken = await getValidAccessToken(); options.headers.Authorization = `Bearer ${accessToken}`; return fetch(uri, options); }, }), cache: new InMemoryCache(), });
Adicione o cliente Apollo ao seu aplicativo
O objeto Apollo client
agora está configurado para enviar solicitações autenticadas do GraphQL para seu back-end do App Services. Tudo o que resta a fazer é disponibilizá-lo para o resto do seu aplicativo React.
O pacote @apollo/client
exporta um componente ApolloProvider
que disponibiliza o client
para qualquer gancho Apollo que você chame de componentes secundários. Envolva seu aplicativo em um ApolloProvider
e passe o objeto client
para o provedor.
// ... code to create the GraphQL client const AppWithApollo = () => ( <ApolloProvider client={client}> <App /> </ApolloProvider> );
Executar queries e mutações
O pacote @apollo/client
inclui um conjunto de ganchos React declarativos que conectam seus componentes à API GraphQL e gerenciam a execução de consultas e mutações.
Para definir consultas e mutações que você pode passar para os ganchos, instale graphql-tag:
npm install graphql-tag
Importe os ganchos relevantes e o construtor de consulta GraphQL na parte superior do arquivo onde você os está usando.
// import whichever Apollo hooks you're using import { useQuery, useMutation } from "@apollo/client"; import gql from "graphql-tag";
Observação
Os ganchos Apollo devem ter um ApolloProvider
Os componentes que chamam os ganchos de consulta e mutação devem ser descendentes do ApolloProvider que você configurou para o Atlas App Services backend do . Os ganchos chamam a consulta e mutação no client
objeto fornecido.
Executar uma consulta
O cliente Apollo inclui dois ganchos para executar consultas. Os ganchos aceitam parâmetros idênticos, mas diferem quando executam a consulta:
useQuery() é executado automaticamente quando seu componente é montado. Ele também retorna uma chamada de respostas que executa novamente a consulta sempre que você a chama.
useLazyQuery() retorna uma função chamada de respostas que executa a consulta sempre que você a chama. Ele não executa a consulta na montagem do componente.
Ambos os ganchos aceitam uma definição de consulta e opções adicionais, incluindo variables
que Apollo passa para a consulta. Ambos também retornam informações sobre o status de execução atual da consulta e os dados retornados da execução mais recente.
const ALL_MOVIES = gql` query AllMovies { movies { _id title year runtime } } `; // Must be rendered inside of an ApolloProvider function Movies() { const { loading, error, data } = useQuery(ALL_MOVIES); if (loading) { return <div>loading</div>; } if (error) { return <div>encountered an error: {error}</div>; } return <MovieList movies={data.movies} />; }
Execute uma mutação
O método useMutation() hook aceita uma definição de mutação e um objeto de configuração opcional. A opção mais comum que você precisará passar é um variables
objeto que mapeia para variáveis GraphQL na definição de mutação.
O gancho retorna vários objetos em uma matriz:
uma função de retorno de chamada que executa a mutação
um objeto que inclui informações sobre o status de execução da mutação e os dados retornados da execução mais recente.
const UPDATE_MOVIE_TITLE = gql` mutation UpdateMovieTitle($oldTitle: String!, $newTitle: String!) { updateOneMovie(query: { title: $oldTitle }, set: { title: $newTitle }) { title year } } `; // Must be rendered inside of an ApolloProvider function MovieList({ movies }) { const [updateMovieTitle] = useMutation(UPDATE_MOVIE_TITLE); return ( <ul> {movies.map((movie) => ( <li key={movie._id}> <div>{movie.title}</div> <button onClick={() => { updateMovieTitle({ variables: { oldTitle: movie.title, newTitle: "Some New Title", }, }); }} > Update Title </button> </li> ))} </ul> ); }
Paginate Data
Você pode paginar dados em suas consultas com os tipos fornecidos pelo esquema GraphQL gerado pela API. Você pode paginar dados com o cliente Apollo GraphQL usando os ganchos useQuery()
e useLazyQueryHook()
.
A API Atlas GraphQL não tem um operador offset
, como a documentação GraphQL recomenda para paginação.
O exemplo abaixo usa o hook useQuery()
e uma query GraphQL que pode fazer query de dados em order crescente e decrescente, dependendo das variáveis que você passar para ela.
const PAGINATE_MOVIES = gql` query PaginateMovies( $prevTitle: String $nextTitle: String $limit: Int! $sortDirection: MovieSortByInput! ) { movies( # Can add other query filters here if you'd like query: { title_gt: $prevTitle, title_lt: $nextTitle } limit: $limit sortBy: $sortDirection ) { _id title year } } `; const resultsPerPage = 5; function PaginateMovies() { const [variables, setVariables] = useState({ prevTitle: undefined, nextTitle: undefined, limit: resultsPerPage, sortDirection: "TITLE_ASC", }); const [firstTitle, setFirstTitle] = useState(); const { data, error, loading } = useQuery(PAGINATE_MOVIES, { variables, }); const [pagePreviousDisabled, setPagePreviousDisabled] = useState(true); const [pageNextDisabled, setPageNextDisabled] = useState(false); useEffect(() => { if (data?.movies?.length && firstTitle === undefined) { setFirstTitle(data.movies[0].title); } setPagePreviousDisabled(false); if (data?.movies?.length < resultsPerPage) { setPageNextDisabled(true); setPagePreviousDisabled(false); } if ( variables.prevTitle === undefined || data?.movies[0]?.title === firstTitle ) { setPagePreviousDisabled(true); setPageNextDisabled(false); } }, [data, data?.movies?.length, firstTitle, variables.prevTitle]); if (loading) { return <div>loading</div>; } if (error) { return <div>encountered an error: {error.message}</div>; } function goToNextPage() { setVariables({ nextTitle: undefined, prevTitle: data.movies[data.movies.length - 1].title, limit: resultsPerPage, sortDirection: "TITLE_ASC", }); } function goToPrevPage() { setVariables({ nextTitle: data.movies[0].title, prevTitle: undefined, limit: resultsPerPage, sortDirection: "TITLE_DESC", }); } const sorted = data.movies.sort((a, b) => { const titleA = a.title.toUpperCase(); // ignore upper and lowercase const titleB = b.title.toUpperCase(); // ignore upper and lowercase if (titleA < titleB) { return -1; // titleA comes first } if (titleA > titleB) { return 1; // titleB comes first } }); return ( <div> <h1>Movies</h1> {data?.movies?.length ? ( sorted.map((movie) => ( <div key={movie._id}> <h3>{movie.title}</h3> <p>Year Published: {" " + movie.year}</p> <br /> </div> )) ) : ( <p>No movies in system</p> )} <div> <button disabled={pagePreviousDisabled} onClick={goToPrevPage}> ← Previous Page </button> <button disabled={pageNextDisabled} onClick={goToNextPage}> Next Page → </button> </div> </div> ); }
Atualizar tokens de acesso
Ao usar o Realm GraphQL e um cliente Apollo, os tokens de acesso expiram 30 minutos após serem concedidos. Você pode atualizar tokens de acesso do usuário com o método refreshAccessToken()
do Realm Web SDK.
// Connect to your MongoDB Realm app const app = new Realm.App(APP_ID); // Gets a valid Realm user access token to authenticate requests async function getValidAccessToken() { // Guarantee that there's a logged in user with a valid access token if (!app.currentUser) { // If no user is logged in, log in an anonymous user. The logged in user will have a valid // access token. await app.logIn(Realm.Credentials.anonymous()); } else { // An already logged in user's access token might be stale. Tokens must be refreshed after // 30 minutes. To guarantee that the token is valid, we refresh the user's access token. await app.currentUser.refreshAccessToken(); } return app.currentUser.accessToken; } // Configure the ApolloClient to connect to your app's GraphQL endpoint const client = new ApolloClient({ link: new HttpLink({ uri: `https://services.cloud.mongodb.com/api/client/v2.0/app/${APP_ID}/graphql`, // We define a custom fetch handler for the Apollo client that lets us authenticate GraphQL requests. // The function intercepts every Apollo HTTP request and adds an Authorization header with a valid // access token before sending the request. fetch: async (uri, options) => { const accessToken = await getValidAccessToken(); options.headers.Authorization = `Bearer ${accessToken}`; return fetch(uri, options); }, }), cache: new InMemoryCache(), });