
GraphQL vem expandindo rapidamente a sua popularidade como padrão para APIs no backend de aplicações web. E uma vez que as chamadas ao GraphQL ainda são nativamente HTTP e você possa consumir as APIs GraphQL usando clientes HTTP como fetch e Axios, é inegável que existem vantagens sólidas de fazê-lo usando clientes específicos para essa tecnologia, principalmente se planeja utilizar TypeScript em conjunto para tipificar os schemas corretamente e deixar tudo type-safe.
Pensando nisso, no tutorial de hoje eu vou te ensinar como consumir APIs GraphQL em aplicações ReactJS com TypeScript e o Apollo Client, sem sombra de dúvida a solução mais popular do mercado para essa finalidade.
É importante entender que este tutorial é voltado a quem já sabe o básico de React, de TypeScript e de GraphQL. Você pode obter esse conhecimento básico nos links em cada nome.
Vamos lá!
#1 – Setup Inicial
Primeiramente você vai precisar ter uma API GraphQL rodando na sua máquina. Caso não possua nenhuma, use essa aqui, que possui queries e mutations para gestão de usuários. Você pode deixá-la rodando facilmente na sua máquina em uma janela de terminal usando o comando ‘npm start’ após instalar as dependências, sendo que ela vai ficar disponível em http://localhost:3000/graphql já com 100 users cadastrados in-memory.
Segundo, você vai precisar de uma aplicação ReactJS. Eu estarei utilizando uma criada com o toolkit Vite nesse meu exemplo e você pode criar uma também com o comando abaixo.
npm create vite@latestUsei como nome ‘react-graphql’, escolhi React, TypeScript e pronto, sua aplicação já vai estar rodando em http://localhost:5173.
Você precisará instalar algumas dependências na sua aplicação React, a saber:
npm install @apollo/client graphqlA biblioteca graphql é a padrão para todas as demais bibliotecas desse protocolo, sendo que não usaremos ela diretamente, é uma dependência da Apollo Client. Esta por sua vez (@apollo/client) expõe pra gente um cliente de comunicação com GraphQL, bem como React Hooks, types e alguns recursos para definição das queries e mutations como veremos adiante.
Crie um arquivo .env na raiz da aplicação e dentro dele uma única variável de ambiente que aponta para a URL da sua API GraphQL.
VITE_GRAPHQL_API_URL=http://localhost:3000/graphqlDepois crie uma pasta src/graphql e dentro dela um client.ts, onde configuraremos o Apollo Client, como abaixo.
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client/core'
const graphqlUri = import.meta.env.VITE_GRAPHQL_API_URL
const httpLink = new HttpLink({ uri: graphqlUri })
const authLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('authToken')
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
Authorization: token ? `Bearer ${token}` : '',
},
}))
return forward(operation)
})
export const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})Começamos carregando a URL e iniciando um HttpLink com ela. Depois, eu defini um ApolloLink, que é algo opcional. Ele serve para criar o comportamento automático de carregar um token no localStorage para incluir no cabeçalho Authorization de todas requisições. Nesse tutorial não vamos usar realmente isso mas é um exemplo importante porque muitas APIs GraphQL possuem rotas autenticadas.
Por fim, inicializamos o ApolloClient em si, passando o link e o cache, o que vai beneficiar a experiência do usuário ao usar a nossa aplicação. Esse ApolloClient deve ser informado em um contexto que vai permitir depois o uso de ReactHooks do Apollo em nossa aplicação. Abaixo como fica a configuração do contexto no main.tsx.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ApolloProvider } from '@apollo/client/react'
import App from './App.tsx'
import { apolloClient } from './graphql/client.ts'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ApolloProvider client={apolloClient}>
<App />
</ApolloProvider>
</StrictMode>,
)Também vale colocar nessa pasta src/graphql um User.ts para o nosso type de User que vai ser usado em diversos locais.
export type User = {
id: string
name: string
email: string
}E por fim, nosso App.tsx vai exibir outros dois componentes que ainda não temos, um para listar usuários, outro para cadastrar usuários.
import UserForm from './components/UserForm'
import UsersList from './components/UsersList'
function App() {
return (
<>
<h1>React + GraphQL</h1>
<hr />
<UserForm />
<UsersList />
</>
)
}
export default AppE com isso terminamos o setup inicial.
#2 – GraphQL Query via ReactJS
Basicamente o que você precisa aprender é a fazer dois tipos de operações através do ReactJS utilizando ApolloClient: Query e Mutation. Assim, vamos fazer um de cada apenas para evitar repetições desnecessárias, mas havendo interesse em praticar, pode olhar o schema da API de exemplo e implementar toda ela repassando os passos deste tutorial. Aliás, isso é um ótimo exercício.
Nossa Query será uma consulta a getUsers, retornando x usuários de uma determinada página. Para fins de organização, vamos criar um arquivo graphql/queries.ts, onde vamos exportar uma constante com a query getUsers tipada.
import { gql } from '@apollo/client/core'
export const GET_USERS = gql`
query GetUsers($page: Int, $pageSize: Int) {
getUsers(page: $page, pageSize: $pageSize) {
id
name
email
}
}
`Se você nunca usou Tagged Templates antes pode achar o uso da função gql um tanto estranha. Basicamente ele é um Template String tradicional, mas usando uma tag function para processar a substituição dos placeholders internos do template. O conteúdo do template em si dispensa apresentações, é exatamente a mesma sintaxe que você veria usando o Postman para testar essa API GraphQL, por exemplo.
Agora antes de fazermos o componente que vai realizar a query acima, como ele é uma listagem de users, vamos criar primeiro o subcomponente que exibe apenas os dados de um usuário. Crie um src/components/UserItem.tsx com o seguinte conteúdo:
type UserItemProps = {
user: {
id: string
name: string
email: string
}
}
export default function UserItem({ user }: UserItemProps) {
return (
<tr>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>
)
}Nenhuma explicação é necessária aqui. Agora vamos importar esse componente no topo do novo arquivo src/components/UsersList.tsx que você vai criar, bem como outras dependências:
import type { TypedDocumentNode } from '@apollo/client/core'
import { useQuery } from '@apollo/client/react'
import { GET_USERS } from '../graphql/queries.ts'
import UserItem from './UserItem.tsx'
import { type User } from '../graphql/User.ts'Também vamos precisar neste mesmo arquivo de alguns types e algumas constantes, todos sendo usados mais adiante.
type GetUsersData = {
getUsers: User[]
}
type GetUsersVars = {
page?: number
pageSize?: number
}
const FIRST_PAGE = 1
const PAGE_SIZE = 10Dentro da function UsersList, precisamos declarar três coisas, que explico a seguir.
const getUsersQuery = GET_USERS as TypedDocumentNode<GetUsersData, GetUsersVars>
const { data, loading, error } = useQuery(getUsersQuery, {
variables: {
page: FIRST_PAGE,
pageSize: PAGE_SIZE,
},
})
const users = data?.getUsers ?? []Primeiro, vamos usar a query GET_USERS que criamos anteriormente com auxílio do gql tipando-a com TypedDocumentNode. Isso vai garantir type-safety e também um autocomplete preciso no passagem de parâmetros e no uso de retorno de queries. Passando a getUsersQuery tipada para o hook useQuery, nós enviaremos via Apollo Client a consulta correta, informando a página que queremos carregar e a quantidade de elementos. Como retorno, temos data (os itens), loading (true ou false indicado se está aguardando retorno) e error (somente preenchido se der erro na consulta).
Com essas três informações obtidas com o hook useQuery, nós temos tudo que é necessário para renderizar os usuários na página, inclusive comecei fazendo um fallback para array vazio caso não tenhamos ainda nenhum usuário em data.
Agora falando da renderização em si, vou criar uma tabela muuuito simples, apenas para vermos o resultado na tela, tratando também os estados de loading e error.
return (
<>
<h2>Users List</h2>
{loading && <p>Loading users...</p>}
{error && <p>Failed to load users: {error.message}</p>}
{!loading && !error && users.length === 0 && <p>No users found.</p>}
{!loading && !error && users.length > 0 && (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<UserItem key={user.id} user={user} />
))}
</tbody>
</table>
)}
<hr />
</>
)O resultado você confere no browser se tudo deu certo.

#3 – GraphQL Mutation via ReactJS
Agora que aprendemos como fazer uma query GraphQL via ReactJS e Apollo Client, vamos ver como fazer uma Mutation, ou sejam, operações que alteram dados.
O processo é bem parecido, mas para fins de organização, vamos guardar as mutations em um novo arquivo src/graphql/mutations.ts com o conteúdo abaixo.
import { gql } from '@apollo/client/core'
export const CREATE_USER = gql`
mutation CreateUser($userInput: UserInputData) {
createUser(userInput: $userInput) {
id
name
email
}
}
`Note que não é muito diferente das queries não, então dispensamos explicações adicionais, considerando que é esperado que você já conheça a sintaxe básica do GraphQL.
Agora vamos criar um src/components/UserForm.tsx com as importações necessárias:
import { useState } from 'react'
import type { TypedDocumentNode } from '@apollo/client/core'
import { useMutation } from '@apollo/client/react'
import { CREATE_USER } from '../graphql/mutations.ts'
import type { User } from '../graphql/User.ts'E depois os types que este componente precisa:
type CreateUserData = {
createUser: User
}
type CreateUserVars = {
name: string
email: string
password: string
}Por fim, dentro da função UserForm, vamos definir o state do usuário que vai ser cadastrado, a variável da mutation que criamos anteriormente, devidamente tipada com TypedDocumentNode e o uso do hook useMutation, que explico melhor mais adiante.
const [user, setUser] = useState<CreateUserVars>({} as CreateUserVars)
const createUserMutation = CREATE_USER as TypedDocumentNode<CreateUserData, CreateUserVars>
const [createUser, { data, loading, error }] = useMutation(createUserMutation)Diferente do hook useQuery, que retorna logo de cara os dados da consulta realizada, o hook useMutation retorna uma função com o nome da mutation (createUser) que deve ser chamada sempre que você quiser enviar a requisição de mutação pra API. Assim, devemos usar esse createUser dentro do click de algum botão de submit do formulário de cadastro, que vamos criar logo mais.
A função de click do botão pode ser algo como abaixo, prevenindo o submit padrão e enviando as propriedades do state do usuário que vai ser preenchido.
async function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
event.preventDefault()
await createUser({
variables: {
name: user.name.trim(),
email: user.email.trim(),
password: user.password.trim(),
},
})
setUser({} as CreateUserVars)
}Já a renderização do formulário é bem simples neste exemplo e não tem nenhuma novidade dado que você já deve ter conhecimento básico de React para acompanhar este tutorial.
return (
<>
<h2>New User</h2>
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
value={user.name}
onChange={handleChange}
required
/>
<br />
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
value={user.email}
onChange={handleChange}
required
/>
<br />
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
value={user.password}
onChange={handleChange}
required
/>
<br />
<button type="submit" disabled={loading}>
{loading ? 'Creating user...' : 'Create user'}
</button>
</form>
{error && <p>Failed to create user: {error.message}</p>}
{data?.createUser && <p>User created: {data.createUser.name}</p>}
<hr />
</>
)Note que usei acima uma função para o binding dos campos com o state de user, que represento abaixo.
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
const { name, value } = event.target
setUser((currentUser) => ({ ...currentUser, [name]: value }))
}Vale ressaltar também o uso adequado dos states de data (retorno da mutation), error (em caso de erro na mutation) e de loading (indicando que a requisição está aguardando retorno).
O resultado você confere no browser.

Note que como nossa listagem traz os primeiros 10 elementos de um total de 100 que tem por padrão na API (mockados), você não vai ver seu usuário na lista, a menos que modifique o código da API ou que mande exibir a página de número 11 na listagem.
E com isso terminamos este tutorial e espero que tenha lhe ajudado a entender como usar ReactJS em conjunto de APIs GraphQL com Apollo Client.
Até a próxima!







