Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
Web SDK

Apollo Client (React) - Web SDK

Puede usar Cliente ApolloPara conectarte a la API GraphQL expuesta de tu aplicación Realm desde una aplicación React. Apollo Client ejecuta consultas y mutaciones, mantiene una caché de datos del lado del cliente y se integra en tu aplicación con componentes y ganchos React idiomáticos.

Nota

Consulta una aplicación de demostración funcional

Consulta el repositorio Realm GraphQL - Apollo (React) en GitHub para ver una aplicación React y Apollo completamente configurada y lista para conectarse a tu backend de Atlas App Services. Utiliza sample_mflix.movies colección que está incluida en los conjuntos de datos de muestra de MongoDB Atlas.

Si no deseas clonar el repositorio, la aplicación de demostración también está disponible en el navegador en Realm GraphQL CodeSandbox.

1

Como en cualquier proyecto de Realm, necesitarás instalar el SDK web de Realm para autenticar usuarios y solicitudes.

npm install realm-web

Apollo agrupa los componentes principales que necesitas para crear un cliente en un paquete llamado @apollo/client. También requiere el paquete graphql para analizar GraphQL queries.

npm install @apollo/client graphql
2

Import the necessary dependencies from realm-web and @apollo/client to create your Apollo GraphQL client and add authentication with Realm.

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

Create a new ApolloClient object that points to your Realm app's GraphQL API endpoint. You generate the endpoint URL based on your Realm App ID or find it on the GraphQL page of the Realm UI.

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

The ApolloClient is configured to send requests to your app. However, all Realm GraphQL requests must include a valid user access token to authenticate requests, so right now any operations sent from Apollo will fail. Access tokens expire after 30 minutes and need to be refreshed.

To authenticate requests, you need to add an Authorization header with a valid Realm user access token to each GraphQL request.

Puedes autenticar a un usuario y obtener su token de acceso con el Realm Web SDK. El objeto HttpLink Apollo te permite agregar headers personalizados a cada solicitud definiendo una función 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

The Apollo client object is now configured to send authenticated GraphQL requests to your App Services backend. All that's left to do is make it available to the rest of your React application.

The @apollo/client package exports an ApolloProvider component that makes the client available to any Apollo hooks that you call from child components. Wrap your app in an ApolloProvider and pass the client object to the provider.

// ... code to create the GraphQL client
const AppWithApollo = () => (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);

El paquete @apollo/client incluye un conjunto de hooks declarativos de React que conectan tus componentes con la API de GraphQL y gestionan la ejecución de consultas y mutaciones.

Para definir consultas y mutaciones que puedas pasar a los hooks, instala graphql-tag:

npm install graphql-tag

Importa los hooks relevantes y el constructor de la consulta GraphQL en la parte superior del archivo donde los vayas a utilizar.

// import whichever Apollo hooks you're using
import { useQuery, useMutation } from "@apollo/client";
import gql from "graphql-tag";

Nota

Los hooks de Apollo deben tener un ApolloProvider

Components that call the query and mutation hooks must be descendants of the ApolloProvider that you configured for your App Services backend. The hooks call the query and mutation methods on the provided client object.

Apollo Client includes two hooks for executing queries. The hooks accept identical parameters but differ in when they execute the query:

  • useQuery() runs automatically when its component mounts. It also returns a callback that re-runs the query whenever you call it.

  • useLazyQuery() returns a callback function that executes the query whenever you call it. It does not run the query on component mount.

Both hooks accept a query definition and additional options, including variables that Apollo passes to the query. They also both return information about the query's current execution status and data returned from the most recent execution.

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

The useMutation() hook accepts a mutation definition and an optional configuration object. The most common option you'll need to pass is a variables object that maps to GraphQL variables in the mutation definition.

The hook returns several objects in an array:

  • una función de retorno que ejecuta la mutación

  • an object that includes information on the mutation's execution status and data returned from the most recent execution.

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

Puede paginar datos en sus consultas con los tipos proporcionados por el esquema GraphQL generado por su API. Puede paginar datos con el cliente GraphQL de Apollo mediante los ganchos useQuery() y useLazyQueryHook().

La Atlas GraphQL API no tiene un operador offset, como recomienda la documentación de GraphQL para la paginación.

El siguiente ejemplo utiliza el hook useQuery() y una query GraphQL que puede consultar datos en orden ascendente y descendente, dependiendo de las variables que pases.

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

Al usar Realm GraphQL y un Apollo Client, los tokens de acceso expiran 30 minutos después de ser otorgados. Puedes refrescar los tokens de acceso de usuario con el método refreshAccessToken() del 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(),
});

Volver

Query MongoDB

En esta página