Menu Docs

Cliente Apollo (React) - SDK da Web

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.

1

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
2

Importe as dependĆŖncias necessĆ”rias de realm-web e @apollo/client para criar seu cliente Apollo GraphQL e adicionar autenticaĆ§Ć£o com o Realm.

import * as Realm from "realm-web";
import {
ApolloClient,
ApolloProvider,
HttpLink,
InMemoryCache,
} from "@apollo/client";
3

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(),
});
4

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(),
});
5

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>
);

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.

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} />;
}

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>
);
}

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}>
&larr; Previous Page
</button>
<button disabled={pageNextDisabled} onClick={goToNextPage}>
Next Page &rarr;
</button>
</div>
</div>
);
}

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(),
});