Automatizando seu Frontend com Next, GraphQL, Apollo e Codegen
Última atualização:
Índice:
Hoje em dia o GraphQL se tornou uma ferramenta que gera uma flexibilidade gigantesca na criação de rotas de APIs (justamente por não as ter), o que deixa ainda mais performático com outras ferramentas que criam toda a lógica do GraphQL para você, tornando a criação de um backend para MVPs extremamente simples e rápida. Vamos explorar como tornar o nosso frontend tão performático quanto, automatizando toda a geração de hooks e funções para chamadas server e client side.
Setup projeto em Next
Primeiramente, criaremos um projeto em Next, pois o intuito é automatizarmos tanto as lógicas de chamas client side quanto server side, e para isso, executamos o comando abaixo:
yarn create next-app graphql-next --typescript
Setup Apollo
Agora, vamos instalar tanto o GraphQL, quanto o Apollo Client na nossa aplicação:
yarn add graphql@15 @apollo/client
Instalamos o GraphQL na versão 15, pois o Apollo e o Codegen ainda não suportam as versões mais novas.
Nos meus projetos next, costumo colocar todas as pastas dentro da pasta src, com exceção da pasta public. Dentro da nossa src, vamos criar uma pasta “lib”, onde criaremos um arquivo “apollo.ts”. Nesse arquivo, vamos criar as configurações do client do Apollo:
Para que o Apollo funcione em qualquer lugar da nossa aplicação, precisamos colocar o nosso ApolloProvider no nosso _app.tsx, e passar o apolloClient que acabamos de criar, da seguinte forma:
// pages/_app.tsx
import type { AppProps } from 'next/app'
import { ApolloProvider } from '@apollo/client'
import { apolloClient } from '../lib/apollo'
function MyApp({ Component, pageProps }: AppProps) {
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
)
}
export default MyApp
Agora, dentro da pasta pages, vamos criar uma query no nosso index.tsx apenas para testar a implementação do Apollo:
// pages/index.tsx
import { gql, useQuery } from '@apollo/client'
import type { NextPage } from 'next'
const GET_CHARACTERS = gql`
query {
characters {
results {
name
}
}
}
`
const Home: NextPage = () => {
const { data } = useQuery(GET_CHARACTERS)
return (
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
export default Home
Note que no modelo tradicional do Apollo, utilizamos o hook useQuery passando a nossa query. A ideia é que de acordo com schema do nosso GraphQL, todos os hooks com cada uma dessas queries sejam gerados automaticamente, tanto para serem utilizadas no client side quanto no server side.
Dessa forma teremos o seguinte retorno na nossa página inicial:
Codegen
Chegou a hora de ver a mágica acontecer! Como falamos anteriormente, o Codegen criará hooks usando o useQuery que utilizamos anteriormente. Mas, já passando todas as queries prontas e, o melhor de tudo, tipadas!
Esse arquivo basicamente configura todos os plugins que instalamos anteriormente, indica onde estão as queries que iremos criar, onde está o nosso cliente e qual o endereço do nosso schema graphql.
Vamos criar um script no nosso package.json para rodar esse arquivo:
Por fim, executamos o nosso script para gerar os nossos hooks, funções e toda a tipagem:
yarn codegen
Então será gerado as seguintes pastas e arquivos no seu projeto:
Agora vamos testar nossos novos hooks. Vamos voltar ao nosso index.tsx, deletar nossa query e o useQuery, e utilizar o nosso useGetCharactersQuery:
// index.tsx
import type { NextPage } from 'next'
import { useGetCharactersQuery } from '../graphql/generated/graphql'
const Home: NextPage = () => {
const { data } = useGetCharactersQuery()
return (
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
export default Home
Se tudo estiver funcionando, você verá que nada mudou e que a nossa query continua funcionando normalmente. Vale ressaltar que agora o nosso data está todo tipado e já sabemos exatamente a estrutura que virá do nossa query.
SSR
Para utilizar as nossas queries no lado do servidor, vamos precisar fazer algumas alterações no nosso client do Apollo e transformar nosso provider em um HOC (High Order Component). Ou seja, iremos criar um componente que recebe outro componente e encapsula esse componente no nosso ApolloProvider.
Basicamente, transformaremos o nosso apolloClient nesse HOC e renomearemos para withApollo:
Se você notar, vai ver que existem alguns detalhes a mais na implementação do withApollo, como um restore do cache e um contexto (ctx) que não é usado em lugar nenhum. Mas, isso são parâmetros que o próprio Apollo e o Codegen usam nas suas implementações. Sem o restore do cache, o SSR não funciona, pois o Apollo armazena as queries SSR nesse cache. E, sem esse ctx, o Codegen também implementa suas funções errado, já que nas funções ele utiliza tanto esse ctx, quanto a exportação que fizemos do tipo “ApolloClientContext”.
Agora, vamos remover o ApolloProvider do _app.tsx:
// _app.tsx
import type { AppProps } from 'next/app'
function MyApp({ Component, pageProps }: AppProps) {
return (
<Component {...pageProps} />
)
}
export default MyApp
E no nosso index, vamos encapsular a exportação do nosso componente com o withApollo:
// index.tsx
import type { NextPage } from 'next'
import { useGetCharactersQuery } from '../graphql/generated/graphql'
import { withApollo } from '../lib/withApollo'
const Home: NextPage = () => {
const { data } = useGetCharactersQuery()
return (
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
export default withApollo(Home)
Tudo estará funcionando normalmente.
Realizando nossa chamada SSR
Para realizarmos a nossa query pelo lado do servidor, basta apenas fazermos mais um encapsulamento no nosso HOC do withApollo e depois chamar uma função que gera as nossas props do SSR já com o nosso data:
// index.tsx
import type { GetServerSideProps, NextPage } from 'next'
import { GetCharactersQuery } from '../graphql/generated/graphql'
import { getServerPageGetCharacters, ssrGetCharacters } from '../graphql/generated/page'
import { withApollo } from '../lib/withApollo'
type HomeProps = {
data: GetCharactersQuery
}
const Home: NextPage<HomeProps> = ({ data }) => {
return (
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
export const getServerSideProps: GetServerSideProps = async (ctx) => {
return getServerPageGetCharacters({}, ctx)
}
export default withApollo(ssrGetCharacters.withPage()(Home))
Assim, vamos receber dentro das nossas props o data com resultado da nossa query.
Conclusão
Dessa forma, basta apenas escrever as nossas queries uma por uma e colocar na nossa pasta, gerar todos os hooks e funções com o codegen, e ir utilizando tanto no client side, quanto no server side da nossa aplicação. Esse setup facilita bastante o desenvolvimento, principalmente porque não temos que ficar criando tipos e importando queries pra lá e pra cá, já que o codegen faz todo esse trabalho para nós.
Espero que tenham gostado. Para acessar o código dessa aplicação de exemplo, basta clicar aqui.
No atual cenário de desenvolvimento de software, a pressão por eficiência e velocidade de entrega nunca foi tão intensa. Empresas de todos os tamanhos estão buscando maneiras de acelerar o
Quando falamos em gestão de um time de engenharia de software, os principais desafios que vem à cabeça são como estimar as atividades, e como lidar com as expectativas dos
Se você trabalha na área de engenharia de software, e se interessa por gestão de projetos, com certeza já deve ter ouvido falar na metodologia Shape-up ou no produto desenvolvido