Alura > Cursos de Programação > Cursos de Node.JS > Conteúdos de Node.JS > Primeiras aulas do curso Graphql: criando APIs baseada em esquemas

Graphql: criando APIs baseada em esquemas

Configurando o projeto do zero - Apresentação

Apresentando o curso e o instrutor

Olá! Seja bem-vinda e bem-vindo a este curso. Desta vez, vamos falar sobre GraphQL. Meu nome é Vinícius Neves, sou o desenvolvedor de boina, mas ainda barbudo, que você aprecia aqui na Alura.

Audiodescrição: Vinícius é um homem branco, com barba e cabelo castanho. Ele está usando uma boina e veste uma camisa casual. Está em um ambiente de estúdio com uma decoração moderna ao fundo.

Introduzindo o GraphQL e seus objetivos

A ideia deste curso é implementar uma aplicação de ponta a ponta, um back-end GraphQL. Vamos começar entendendo o mais importante: a motivação por trás da existência do GraphQL.

Vamos explorar de onde o GraphQL veio, qual problema ele resolve e quais são suas limitações. Após essa introdução, vamos colocar a mão na massa e construir uma aplicação. Nesta aplicação, realizaremos diversas atividades ao longo das aulas: criar usuários, fazer login, criar canais, fazer upload de vídeos, comentar em vídeos e até mesmo lidar com comentários em tempo real. Quando alguém cria um comentário, como mostrado na tela, receberemos uma notificação.

Preparando o ambiente e expectativas do curso

É importante que estejamos com o Node atualizado, pois não abordaremos a base do Node aqui. Nosso foco será no GraphQL, especificamente utilizando o framework NestJS. Se estamos prontos para essa jornada, vamos começar a codificar. Este curso aborda muitos conceitos interessantes e cobre de ponta a ponta o que enfrentamos no dia a dia como pessoas desenvolvedoras: criação de usuários, autenticação com token, criação e atualização de dados, entre outros.

Esperamos que todos estejam tão animados quanto nós. Nos vemos no próximo vídeo, onde aprenderemos mais sobre GraphQL.

Configurando o projeto do zero - A eterna batalha do REST vs GraphQL

Introduzindo o contexto histórico do HTTP

Para entrar na discussão sobre Rust versus GraphQL, precisamos entender um contexto histórico. Vamos começar do início, abordando o HTTP. Como funciona esse protocolo? Essencialmente, temos um cliente que faz uma requisição e recebe uma resposta. Esse é o funcionamento básico do HTTP: alguém solicita algo e o servidor responde.

Um exemplo disso é o navegador, que atua como cliente, utilizando a internet para visualizar um site, como o servidor da Alura. Quando digitamos www.alura.com.br, algo acontece que transforma esse endereço compreensível para humanos em números, realizando todo o roteamento necessário, conforme a web funciona nos bastidores. O ponto central aqui é a solicitação e a resposta.

Evoluindo o HTTP e introduzindo o SOAP

Historicamente, o HTTP foi inventado no CERN. O primeiro site, inclusive, ainda está ativo. Até a versão 0.9, o HTTP não aceitava cabeçalhos. Esses cabeçalhos foram introduzidos na versão 1.0, em 1996. Antes, o tráfego era apenas de HTML. Com os cabeçalhos, foi possível adicionar informações extras nas requisições, tanto na ida quanto na volta, conferindo "superpoderes" ao HTTP.

Com isso, surgiram novas formas de programação distribuída. Por exemplo, o XML e o RPC permitiam chamadas de código nos bastidores, enviando mensagens de um lado para o outro. Não era mais necessário trafegar apenas HTML; podíamos enviar XML também. Foi nesse contexto que surgiu o SOAP (Simple Object Access Protocol), que, apesar do nome, não era simples.

No SOAP, instanciávamos uma classe, e o código era executado em outra máquina. Nos bastidores, ocorria a serialização, envio pela rede, processamento, deserialização, e o ciclo se repetia. Embora amigável a nível de código, o SOAP apresentava problemas, como conexões intermitentes e complexidades adicionais.

Explorando os princípios do REST

O importante é que, com os cabeçalhos, surgiram novas formas de utilizar o protocolo, além do HTML, incluindo o XML. Isso nos leva ao REST (Representational State Transfer), que introduz algumas restrições e regras. Quando afirmamos que uma API não é RESTful, podemos gerar discussões, pois as pessoas tendem a defender suas implementações.

RESTful refere-se a uma API que segue os princípios REST. Para ser considerada RESTful, uma API deve aderir a todas essas regras; caso contrário, é apenas JSON sobre HTTP, o que não está errado, mas não é RESTful. Vamos discutir esses princípios.

Detalhando os princípios RESTful

Primeiro, temos o modelo cliente-servidor, onde o cliente faz um pedido, o servidor processa e devolve. Além disso, o REST é stateless (sem estado), o que significa que cada requisição deve conter todas as informações necessárias para seu funcionamento, sem manter estado entre requisições.

Outro princípio é a possibilidade de cache, permitindo adicionar uma camada de cache. O REST também deve ser em camadas, compreendendo todas as camadas relacionadas à API. Um conceito mais intangível é o Code On Demand, que ocorre quando carregamos HTML, CSS e scripts JavaScript do servidor, exemplificando o Code On Demand.

Implementando a interface uniforme e HATEOAS

Estamos solicitando o código e vamos executá-lo sob demanda. Como o REST não foi originalmente pensado como uma API, mas sim para a web, podemos ter essa necessidade de código sob demanda. Um ponto crítico do HTTP, que muitas APIs não possuem, mas que para ser RESTful deveria ter, é a interface uniforme. Basicamente, precisamos conseguir fazer um controle de hipermídia de forma que cada requisição nos forneça informações sobre o que pode acontecer com aquele recurso ou sobre a navegação em si. Isso se relaciona com o conceito de HATEOAS (Hypermedia as the Engine of Application State).

Vamos entender isso na prática. Observemos uma requisição feita no Postman. Solicitamos o produto de id1. Além de trazer o id, o nome, o preço e a descrição desse produto, há uma propriedade _links, que contém um link para ele mesmo e outra propriedade que é produtos relacionados. A ideia de hipermídia é justamente essa, de conseguirmos navegar pelos recursos.

Comparando REST e GraphQL

No final, queremos sair do POX (Plain Old XML) para alcançar a glória do REST. Vamos deixar de usar XML, começar a adicionar recursos, observar os verbos HTTP de acordo com o tipo de operação que queremos realizar, adicionar hipermídia e, assim, atingir a glória do REST.

Vamos agora refletir sobre um exemplo prático. Trouxemos uma tela do YouTube do canal da Alura. Podemos pensar em como estruturaríamos endpoints REST. Poderíamos ter, por exemplo, /channels para listar todos os canais, /channels/{id} para obter os dados de um canal específico, /channels/{id}/playlists para trazer todas as playlists daquele canal específico, /playlists/{id} para obter uma playlist específica, e /playlists/{id}/videos para obter os vídeos daquela playlist. Percebemos a quantidade de requisições necessárias para montar essa tela. É aqui que o REST pode se tornar complexo, com um excesso de round trips. Quantas vezes precisamos acessar o servidor para obter os dados necessários para montar essa tela?

Introduzindo o GraphQL

O GraphQL, criado em 2012, busca resolver esse problema. Ele visa resolver o excesso de round trips e facilitar a construção da interface. Além disso, o GraphQL se preocupa com o overfetching e underfetching. Isso significa que não queremos obter dados em excesso nem em falta. Se temos uma lista e queremos listar apenas os canais, não queremos trazer todos os vídeos de todos os canais, pois não sabemos para onde a pessoa irá navegar. Queremos trafegar apenas os dados necessários, sob medida.

Explorando a flexibilidade do GraphQL

Voltando ao exemplo do YouTube, gostaríamos de poder especificar que, para esse cenário, queremos apenas a thumbnail, o título, o número de visualizações, o nome do canal e a data de publicação. Nada além disso. Se a pessoa clicar, então pedimos mais informações. A ideia do GraphQL é ser algo sob medida.

Se fizermos uma requisição GET tradicional em uma API HTTP, como por exemplo para pessoa/1, o que retorna é o nome Maria, sobrenome da Silva, e-mail maria.silva@gmail.com, e o estado civil. Por padrão, é isso que uma API REST entregaria. No GraphQL, pedimos explicitamente quais informações queremos. Se estamos fazendo uma query para obter dados de uma pessoa, especificamos os campos desejados. Podemos, por exemplo, solicitar apenas o nome e o sobrenome, sem precisar do e-mail ou estado civil. Assim, entregamos exatamente os dados necessários.

Concluindo a discussão sobre GraphQL

No nosso cenário, podemos solicitar dados de um canal cujo nome de usuário é Alura e obter informações específicas do canal, como o nome, a quantidade de inscritos, as playlists disponíveis, o nome das playlists, e dois vídeos com seus títulos e quantidade de visualizações. É algo bem sob medida.

Concluindo, conseguimos entender qual problema estamos tentando resolver com uma API GraphQL. Queremos evitar o excesso de round trips e também cuidar do overfetching e underfetching. Vamos ser bem granulares e específicos para cada cenário e consumo. Essa é a flexibilidade que o GraphQL oferece.

Agora que entendemos o caso de uso específico, vamos explorar os conceitos necessários para desenvolver uma API GraphQL. Nos vemos na próxima aula.

Configurando o projeto do zero - Conhecendo o servidor GraphQL

Introduzindo o servidor GraphQL

Vamos agora entender o que é um servidor GraphQL. Neste curso, vamos utilizar especificamente o servidor chamado Apollo e o framework Nest para desenvolver um servidor desse tipo. Essa é a técnica que vamos adotar, embora não seja a única. Deixaremos o conteúdo em texto para que possamos entender também as diferenças.

Como funciona essencialmente um servidor GraphQL? Ele é uma API baseada em esquema (schema-based), ou seja, todo o núcleo, o conceito principal, é justamente o esquema. Se pensarmos em uma árvore montando isso, teremos o esquema principal, que é um tipo, um tipo raiz (root type). Também temos os tipos específicos de operações que podemos realizar. Se quisermos buscar dados, faremos uma query. Se quisermos enviar dados, faremos uma mutation. E se quisermos nos inscrever em algo que acontece em tempo real e manter essa conexão aberta, faremos uma subscription. Esses são os três pilares do GraphQL.

Explicando os tipos de GraphQL

Além disso, temos os tipos de objeto (object types), que são os modelos em si. No vídeo anterior, falamos sobre canais e vídeos na tela do YouTube, onde tínhamos vídeos, playlists e canais. Aqui, por exemplo, temos usuários e postagens, que são nossos modelos. Eles serão nossos tipos de objeto. Temos também os tipos escalares (scalar types), que são de fato os tipos das propriedades. Um usuário, por exemplo, terá um id, um name e um e-mail. Estamos falando das propriedades do nosso modelo. Portanto, o tipo raiz é o esquema em si e as operações que podemos realizar. O tipo de objeto são nossos modelos, e o tipo escalar são os tipos de propriedade que nossos modelos podem ter.

Essencialmente, é assim que funciona. Temos dois possíveis enfoques: podemos ter o code first, ou seja, criamos o código e usamos alguma ferramenta para gerar o esquema derivado do código. Ou fazemos o contrário, adotando o schema first e derivando o código a partir do esquema.

Comparando abordagens code first e schema first

No code first, definimos os modelos, os tipos de objeto, diretamente no código e temos ferramentas que geram automaticamente os tipos para nós. Como escolhemos entre um ou outro? Podemos optar por algo baseado em esquema. Imagine que temos um front-end que depende disso; então, fazemos todo o esquema primeiro, compartilhamos e, em cima do esquema, desenvolvemos o servidor. Esse pode ser um cenário plausível. Na prática, o que vemos muito é a abordagem code first, ou seja, utilizamos o TypeScript e o Nest, escrevemos o código em TypeScript e deixamos as ferramentas de automação gerarem o esquema para nós.

Explorando os resolvers no GraphQL

Agora, vamos começar a entrar nos resolvers, ou seja, como vamos desenvolver isso? Enquanto no lado da API REST costumamos ter controllers, no lado do GraphQL temos resolvers. Como isso funciona? O resolver é um intermediário entre o que a pessoa usuária está pedindo, consumindo nossa API, e o que precisamos fazer para devolver uma resposta. Um resolver é basicamente um conjunto de funções. Podemos colocá-lo no nível do controller.

No contexto de uma API REST, utilizamos controllers no back-end. No entanto, no GraphQL, não temos controllers, mas sim resolvers. Além disso, no GraphQL, não consideramos o status code. A resposta será sempre 200, mas o corpo da mensagem conterá os dados necessários para entender a operação realizada. Portanto, não utilizamos o status code para verificar se a operação foi bem-sucedida ou não; analisamos o conteúdo da resposta. Isso ocorre porque abstraímos essa informação para o cliente que consome a API. Na prática, trabalhamos mais orientados aos dados e ao estado do que ao controle granular do que está acontecendo.

Definindo object types e resolvers

Já entendemos o que é um resolver. Agora, vamos falar sobre o object type. Lembramos que discutimos sobre root, object type e scalar type. Quando temos um modelo, uma entidade, utilizamos a anotação do NestJS para definir um object type. Por exemplo, ao criar o object type de uma categoria, definimos um id, um displayName e um icon. O displayName e o icon são do tipo string. O id também é uma string, mas possui um escalar específico.

Para ilustrar isso, vamos ver como definimos um object type no código:

@ObjectType()
export class Category {
    @Field(() => ID)
    id: string;

    @Field()
    displayName: string;

    @Field()
    icon: string;
}

Se temos um object type categoria, criamos um resolver para essa categoria. Utilizamos uma anotação para indicar o que estamos resolvendo. Assim, temos um resolver de categoria, que é uma classe com um nome específico. Esse resolver possui uma query que retorna um array de categorias, ou seja, uma lista de categorias. Essa operação é uma promise que provavelmente acessará um serviço que, por sua vez, acessará o banco de dados, retornando os dados no final.

Implementando resolvers para categorias e produtos

Aqui está como podemos implementar um resolver para a categoria:

@Resolver(() => Category)
export class CategoryResolver {
    @Query(() => [Category])
    async categories(): Promise<Category[]> {
        return [
            {
                id: 'camisetas',
                displayName: 'Camisetas',
                icon: 'https://raw.content.com/icon.png',
            },
        ];
    }
}

É importante evitar duplicações ou acoplamentos desnecessários no código. Por exemplo, em um cenário com categorias e produtos, ao invés de incluir tudo em um único local, podemos ter um resolve field específico para um campo. Quando temos nosso object type, ao invés de um escalar, como um id, podemos apontar para outro objeto e usar um resolve field para resolver esses campos específicos.

Vamos ver como isso se aplica a um resolver de produto:

@Resolver('Product')
export class ProductResolver {
    @Query(() => [Product])
    async getProducts(): Promise<Product[]> {
        return [
            {
                name: 'Tênis Esportivo',
                price: 199.99,
                seller: 'Loja XYZ',
                image: 'image-url',
                category: { id: '1', displayName: 'Calçados' },
            },
        ];
    }

    @ResolveField(() => String)
    async categoryName(@Parent() product: Product): Promise<string> {
        return product.category.displayName;
    }
}

Utilizando o playground do GraphQL

Outra característica comum do GraphQL é o playground, que oferece uma documentação integrada. Não precisamos fazer muito para ter esse playground funcionando, permitindo que façamos queries e mutations sem a necessidade de ferramentas específicas como o Postman.

Por exemplo, podemos executar a seguinte query no playground:

# Write your query or mutation here
query {
    categories {
        displayName
    }
}

E a resposta que obteríamos seria algo assim:

{
    "data": {
        "categories": [
            {
                "displayName": "Camisetas"
            },
            {
                "displayName": "Bolsas"
            },
            {
                "displayName": "Calçados"
            },
            {
                "displayName": "Calças"
            }
        ]
    }
}

Preparando para codificar com NestJS

Com isso, estamos prontos para começar a codificar. Na sequência, vamos colocar a mão no NestJS e construir o back-end de GraphQL. Vamos em frente!

Sobre o curso Graphql: criando APIs baseada em esquemas

O curso Graphql: criando APIs baseada em esquemas possui 247 minutos de vídeos, em um total de 51 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Node.JS acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas