Aviso: não descrevemos em detalhes a “essência mágica” e a história de origem do instrumento (muito já foi falado sobre isso). Esperamos que aqueles que já estão familiarizados com o GraphQL se beneficiem de nossa experiência prática.

Recentemente, participamos da criação de um catálogo para Internet de equipamentos de iluminação. Para criar páginas de publicidade, os administradores do site podem usar o designer: selecionar os blocos necessários (por exemplo, banners ou listas), preenchê-los com dados, definir a ordem de exibição e outras configurações. Ao mesmo tempo, o aplicativo renderizou componentes que foram pré-definidos para cada tipo de bloco.
Em cada categoria do catálogo online, cartões de vários grupos de produtos foram exibidos e, quando você passa o mouse sobre o cartão, uma lista de propriedades para os produtos anexados é exibida.
Precisávamos exibir as propriedades dos bens em uma estrutura de árvore e fornecer uma velocidade suficientemente alta de processamento de solicitações.
Escolhemos a seguinte ordem de trabalho com os pedidos:
- Solicite todos os grupos de produtos para uma categoria específica (geralmente cerca de 50 grupos).
- Solicite uma lista de produtos para cada grupo.
- Solicite uma lista de propriedades para cada produto.
Como estávamos desenvolvendo um aplicativo baseado em GraphQL, estávamos preparados para o fato de que alguns dos dados teriam uma estrutura aninhada bastante complexa. Embora ramificar essa estrutura fosse lógico para o desenvolvimento do backend, foi necessário escrever alguma lógica "extra" na frente para processar os dados e enviá-los ao componente, de acordo com o design.
Devido às peculiaridades do construtor GraphQL, decidimos coletar propriedades e valores exclusivos não na parte de trás, mas na frente e, em seguida, renderizá-los em uma ordem específica. No entanto, o processamento da solicitação foi muito lento - até 20 segundos, o que, é claro, não nos convinha.
Por esse motivo, começamos a dividir cada solicitação em pequenas subconsultas e carregar os dados em partes. Como resultado, o aplicativo melhorou visivelmente em velocidade - as solicitações não demoravam mais do que 2 segundos. Embora o número de solicitações tenha aumentado, a carga no sistema diminuiu e a necessidade de carregar dados não utilizados desapareceu.
A seguir, vamos falar com mais detalhes diretamente sobre como trabalhar com GraphQL.
Recursos de trabalhar com GraphQL
Os requisitos do produto eram para usarmos a linguagem de consulta GraphQL desenvolvida pelo Facebook. Por isso, não nos entregamos a discussões intermináveis sobre o que é melhor, GraphQL ou REST - em vez disso, decidimos usar a tecnologia certa da forma mais eficiente, levando em consideração todos os seus pontos fortes.
Levamos em consideração que o GraphQL foi projetado para simplificar o desenvolvimento e a manutenção de APIs, principalmente por ter um único terminal.
GET /news
GET /posts
POST /news
POST /post
GraphQL tem um único ponto de extremidade. Isso significa que não precisamos fazer duas solicitações separadas para obter dados de dois recursos diferentes. O GraphQL consolida todas as solicitações e mutações em um terminal e o disponibiliza para referência, bem como evita o controle de versão inerente às APIs REST.
GraphQL oferece a capacidade de otimizar o desempenho e obter exatamente os dados necessários no momento, usando uma sintaxe de consulta especial: os campos obrigatórios devem ser listados na consulta.
const FETCH_USER_DATA = gql`
query FetchUserData {
user {
firstName
lastName
date
}
}
`;
GraphQL usa entidades fortemente tipadas e esquema de tipo, que por sua vez é útil em conjunto com TypeScript e geração de tipo front-end.
Muitos desses recursos podem certamente ser implementados em APIs REST, no entanto, GraphQL os fornece fora da caixa.
Para a interação do cliente com GraphQL, escolhemos a solução mais popular com boa documentação - a biblioteca Apollo Client, que permite obter, armazenar em cache e modificar os dados do aplicativo. O Apollo Client oferece a capacidade de usar ganchos e ferramentas de solicitação e mutação para rastrear facilmente o status de download / erro.
Também na frente, usamos o framework NextJS, escolhido levando em consideração os seguintes fatores: pré-renderização (NextJS fornece um mecanismo muito simples para implementação de geração estática e SSR fora da caixa), suporte para todas as soluções css-in-js existentes, roteamento dinâmico, suporte para arquivos estáticos (por exemplo, imagens) nos componentes React.
Finalmente, quando as tecnologias forem selecionadas, vamos prosseguir para o desenvolvimento. À primeira vista, tudo parece bem: bibliotecas modernas, boa documentação, muitos casos de uso diferentes. Cada uma das tecnologias individualmente é projetada para facilitar o desenvolvimento confortável e rápido. Ao criar um padrão, que era indispensável, e projetar componentes de IU, gradualmente nos aproximamos do estágio de interação efetiva entre nossas bibliotecas. Aqui toda a diversão começou.
Olhando mais a fundo nos mecanismos do NextJS, vemos que ele usa duas formas de pré-renderizador: geração estática e SSR. Ambas as estratégias são implementadas usando funções especiais de pré-carregamento de dados:
`getInitialProps` (SSR) - quando carregado pela primeira vez, ele roda no servidor, solicita dados e então os passa para o componente como suporte.
function Component ({data}) {
...
}
Component.getInitialProps = async (ctx) => {
const res = await fetch('https://...')
const json = await res.json()
return { data: json.data }
}
`getStaticProps` (Static Generation) - é executado no estágio de construção. NextJS pré-renderiza a página usando os adereços retornados desta função.
export async function getStaticProps(context) {
return {
props: {}, // props
}
}
`getServerSideProps` (Server Side Rendering) - NextJS pré-renderiza a página em cada requisição, usando os dados retornados desta função como suporte.
export async function getServerSideProps(context) {
return {
props: {}, // props
}
}
Assim, todas as funções listadas são declaradas fora do componente, recebem alguns dados e os passam para o componente. Isso leva a um dos problemas de interação com o Apollo Client. A biblioteca fornece mecanismos de consulta como o gancho ʻuseQuery` e o componente `Query`, e nenhum dos métodos propostos pode ser usado fora do componente. Com isso em mente, em nosso projeto decidimos usar a biblioteca next-with-apollo e no final ficamos satisfeitos com o resultado.
Outro problema conhecido com o Apollo Client, que também encontramos, é a possibilidade de um loop infinito de solicitações do gancho ʻuseQuery`. O cerne do problema é que o Apollo Client continua a enviar solicitações indefinidamente até que receba dados com sucesso. Isso pode levar a uma situação em que o componente "trava" em uma solicitação interminável, se o servidor não puder retornar dados por algum motivo.
No nosso caso, um dos motivos foi a mudança no esquema do verso. O Apollo gera os tipos por conta própria, portanto, ao escrever consultas na frente, tivemos que seguir os tipos gerados para evitar bugs. Por exemplo, tínhamos um pedido de trabalho, sem problemas de tipo. No entanto, ao alterar o esquema no painel traseiro, os tipos mudaram ao mesmo tempo, por isso a solicitação de trabalho poderia parar de funcionar. Diante disso, é ideal implementar imediatamente o registro e um sistema claro de tratamento de erros para economizar os nervos e o tempo da equipe.
Em nossa opinião, acabou sendo bastante útil que nas consultas do Graphql você possa especificar exatamente quais dados devem ser recebidos. Ao enviar um grande número de solicitações, isso evita o processamento de dados desnecessários. Por sua vez, isso pode afetar significativamente o desempenho à medida que a quantidade de dados aumenta.
É interessante notar que uma das vantagens do GraphQL é considerada a conveniência de desenvolvimento e suporte API, mas esta propriedade é mais significativa para o desenvolvimento de backend e, de acordo com nossas observações, não tem um impacto significativo na frente. Devido à geração de tipos, sempre que o esquema foi alterado no backplane, reescrevemos todas as consultas afetadas. Beck também teve que refinar o esquema se precisássemos de algo que ainda não havia sido implementado. Ao mesmo tempo, a geração de tipos ao usar o TypeScript tornou possível detectar muitos erros no estágio de escrita do código.
Resumindo
De acordo com nossas observações, GraphQL é amplamente utilizado em vários tipos de soluções de TI e oferece certas vantagens para a equipe de desenvolvimento. Vamos resumir os principais recursos do GraphQL que encontramos durante o desenvolvimento do projeto:
- . graphql , TypeScript .
- . GraphQL - . , ( , ). graphql «» ,
- . graphql- , . .
- . GraphQL , .
! , .