{"id":23810395,"url":"https://github.com/zingarelli/alura-books-typescript","last_synced_at":"2026-05-02T23:43:05.691Z","repository":{"id":228482706,"uuid":"772335981","full_name":"zingarelli/alura-books-typescript","owner":"zingarelli","description":"Evolução do Alura Books em TypeScript e lidando com comunicação com API para autenticação e obtenção de dados.","archived":false,"fork":false,"pushed_at":"2024-04-29T23:11:18.000Z","size":5367,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-11T11:50:28.587Z","etag":null,"topics":["alura","apollo-client","axios","graphql","jwt","react","react-query","typescript","vitrinedev"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zingarelli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-15T01:47:20.000Z","updated_at":"2024-06-28T19:12:36.000Z","dependencies_parsed_at":"2024-04-29T23:49:01.697Z","dependency_job_id":null,"html_url":"https://github.com/zingarelli/alura-books-typescript","commit_stats":null,"previous_names":["zingarelli/alura-books-autenticacao"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zingarelli/alura-books-typescript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zingarelli%2Falura-books-typescript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zingarelli%2Falura-books-typescript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zingarelli%2Falura-books-typescript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zingarelli%2Falura-books-typescript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zingarelli","download_url":"https://codeload.github.com/zingarelli/alura-books-typescript/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zingarelli%2Falura-books-typescript/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32553690,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T22:28:24.418Z","status":"ssl_error","status_checked_at":"2026-05-02T22:28:14.225Z","response_time":132,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alura","apollo-client","axios","graphql","jwt","react","react-query","typescript","vitrinedev"],"created_at":"2025-01-02T00:14:21.189Z","updated_at":"2026-05-02T23:43:05.674Z","avatar_url":"https://github.com/zingarelli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Alura Books versão TypeScript\n\nNeste projeto, temos algumas telas já implementadas para um e-commerce de livros chamado Alura Books. Nosso objetivo é evoluir a aplicação, fazendo requisições a uma API para criar novas telas, implementar políticas de autenticação/autorização, além fazer o fluxo de adicionar e remover itens ao carrinho de compras. Para isso, utilizamos inicialmente Axios, passamos pelo React Query e finalizamos com Apollo Client e GraphQL.\n\n| :placard: Vitrine.Dev |     |\n| -------------  | --- |\n| :sparkles: Nome        | **Evolução Alura Books**\n| :label: Tecnologias | GraphQL, ReactQuery, Apollo Client, Axios, JWT, TypeScript, React\n| :rocket: URL         | \n| :fire: Curso     | https://cursos.alura.com.br/course/react-autenticando-usuarios\n\n\n![](https://github.com/zingarelli/alura-books-autenticacao/assets/19349339/1da53376-fb45-43b7-ba02-b721e983b6b0#vitrinedev)\n\n## Créditos\n\nO projeto foi adaptado a partir deste [repositório da Alura](https://github.com/alura-cursos/curso-react-alurabooks/tree/aula-1), que já traz a páginas e componentes iniciais da Alura Books. \n\nA parte de autenticação e obtenção dos pedidos é feita por meio de consultas a uma API, que é mockada com o uso do json-server e JWT, ou seja, roda localmente. A API pode ser obtida [neste repositório](https://github.com/viniciosneves/api-alurabooks).\n\nUma segunda API mockada é utilizada nos cursos de Apollo Client e GraphQL. O projeto pode ser obtido neste [outro repositório](https://github.com/alura-cursos/alurabooks-gql). \n\n## Detalhes do projeto\n\nEste é um projeto construído ao longo dos cursos da trilha de formação da Alura, chamada de [\"React: consumindo APIs\"](https://cursos.alura.com.br/formacao-react-consumindo-apis). Em cada curso, lidamos com um tópico diferente (autenticação, React Query e GraphQL). Detalhes sobre o projeto e cada tópico aprendido são dados nas seções a seguir.\n\n*Observação:* a formação inicia com um curso sobre desenvolvimento de uma biblioteca de componentes, que incluiu a utilização do Storybook e publicação no NPM. O projeto está separado [neste outro repositório](https://github.com/zingarelli/alura-books-ds), pois houve problemas de incompatibilidade com o projeto inicial disponibilizado para acompanhamento dos outros cursos, então a biblioteca que eu desenvolvi não pôde ser reaproveitada.\n\nO código foi desenvolvido em React com TypeScript. Há comunicação com uma API mockada rodando localmente. Por meio dela é possível fazer o login/cadastro da pessoa usuária, além de requisições para obter dados de pedidos, categorias, livros e autores. Utilizamos tanto o Axios e o [React Query](#react-query), quanto o [Apollo Client e GraphQL](#apollo-client-e-graphql) para fazer requisições e consultas à API. Na [Seção sobre Instalação](#instalação) há detalhes de como instalar e subir cada API.\n\n### Páginas construídas:\n\n#### Modal de Login\n\nA autenticação é feita na API, que informa se foi ou não bem sucedida.\n\n![tela de login](https://github.com/zingarelli/alura-books-typescript/assets/19349339/a4dbab4f-7fd1-4d06-b828-a1a8c521506a)\n\n#### Modal de cadastro\n\nUma requisição POST é enviada à API para registro do usuário.\n\n![tela de cadastro](https://github.com/zingarelli/alura-books-typescript/assets/19349339/83eee302-a750-438f-855c-5d2f7730a482)\n\n\n#### Página de pedidos\n\nSomente pode ser acessada após login. Internamente, é enviado um token de acesso à API para conseguir consultar os pedidos.\n\n![tela da conta do usuário, exibindo os pedidos efetuados](https://github.com/zingarelli/alura-books-typescript/assets/19349339/f4e09b52-5dca-42b9-80e6-b8356902e579)\n\n#### Livros por categoria\n\nAo selecionar uma categoria no menu superior do site, a página é carregada com os livros dessa categoria;\n\n![gif mostrando a seleção de livros da categoria \"Front End\"](https://github.com/zingarelli/alura-books-typescript/assets/19349339/a4011c35-bc54-4d6b-a358-d7d476ebc8bb)\n\n#### Detalhes de um livro\n\nAo clicar no botão \"Ver detalhes\" de um livro na galeria de livros, é carregada uma página com os detalhes desse livro, que traz ainda opções para escolher o formato (e-book, impresso, combo), a quantidade, e um botão para comprar. Quando clicado em \"Comprar\", o livro é adicionado ao carrinho, com a quantidade e formato selecionados;\n\n![git fom a interação de selecionar formato e quantidade de um livro e adicioná-lo ao carrinho ao clickar no botão de comprar](https://github.com/zingarelli/alura-books-typescript/assets/19349339/fb4e8919-5166-4a73-aaef-d34336a10146)\n\n#### Carrinho de compras\n\nPágina interativa que exibe todos os itens adicionados ao carrinho. Nela é possível alterar a quantidade e remover um item. O valor total da compra é atualizado em tempo real. Além da página, é possível também visualizar um \"mini-carrinho\", que é exibido ao clicar no ícone de sacola que fica no topo da aplicação. O mini-carrinho mostra o título e autor dos livros que estão no carrinho, bem como um botão para ver a página completa do carrinho.\n\n![gif com a interação de alterar a quantidade de um livro do carrinho, bem como remover um livro](https://github.com/zingarelli/alura-books-typescript/assets/19349339/d79bb13e-9ce5-401b-8e29-a7c97f25599f)\n\n## Lidando com autenticação\n\nForam desenvolvidas novas telas para a aplicação, com o objetivo de lidar com autenticação e autorização da pessoa usuária. Na parte de autenticação, é feito o login da pessoa por meio de e-mail e senha, e um token é salvo na sessionStorage do navegador. Na parte de autorização, a pessoa pode acessar uma página de pedidos e ver seus pedidos, mas para isso é necessário recuperar esses dados via API, passando o token recebido durante a autenticação. \n\n### Autenticação\n\nUma tela de cadastro foi desenvolvida, de modo a cadastrar nova pessoa usuária. Ao clicar para envio do cadastro, é feita uma chamada POST à API na URL http://localhost:8000/public/registrar. Para o post, são enviadas as seguintes propriedades que a API espera no corpo da requisição: email, senha, nome, endereco, complemento, cep.\n\nO login também é feito via POST, com a URL http://localhost:8000/public/login. A API espera receber um objeto com email e senha. Caso o login seja feito com sucesso, a API retorna na propriedade `data` de um response outras duas propriedades: `access_token` e `user`. É por meio desse `access_token` que lidamos com a autorização da pessoa usuária para recuperar seus pedidos.\n\nExemplo de POST via Axios para fazer o login e salvar o token em um sessionStorage no navegador:\n\n```typescript\nconst usuario = {\n    email,\n    senha\n}\naxios.post('http://localhost:8000/public/login', usuario)\n    .then(resp =\u003e {\n        sessionStorage.setItem('token', resp.data.access_token)\n        aoFechar()\n    })\n    .catch(err =\u003e {\n        if (err?.response?.data?.message) alert(err.response.data.message)\n        else (console.log(err))\n    })\n```\n\n- `sessionStorage`: diferente da `localStorage`, a `sessionStorage` armazena dados no navegador, porém esses dados são **removidos** quando o navegador é fechado ou quando a aba em que a aplicação está rodando é fechada. Caso seja feito um refresh da página, os dados na sessionStorage são **mantidos**.\n\n- atenção: a sessão é relativa e única para cada aba/janela aberta. Ou seja, se a aplicação estiver rodando em **duas (ou mais) abas** diferentes, cada uma terá **sua própria sessão** (tokens distintos). \n\n### Token e autorização\n\nA API libera acesso ao endpoint `/pedidos` somente mediante o **envio do token** que foi entregue após o login bem-sucedido. Essa informação pode ser enviada por meio de uma chamada GET via Axios, passando como segundo parâmetro um objeto com uma propriedade `headers`, a qual também possui um objeto e este possui uma propriedade `Authorization`. Conforme a especificação da API mockada, ela espera receber no `Authorization` o seguinte valor: `Bearer \u003caccess_token\u003e`. Veja o exemplo:\n\n```typescript\nconst urlPedidos = 'http://localhost:8000/pedidos';\nconst access_token = sessionStorage.getItem('token');\naxios.get(urlPedidos, {\n    headers: {\n        'Authorization': `Bearer ${access_token}`\n    }\n})\n    .then(resp =\u003e console.log(resp.data))\n    .catch(err =\u003e console.log(err))\n```\n\nA tela de pedidos permite também a exclusão de um item. Isso é feito por meio de uma chamada DELETE, que também exige um token para que a ação seja feita. O código é semelhante ao do GET:\n\n```ts\nconst excluirPedido = (id: number) =\u003e {\n    axios.delete(`${urlPedidos}/${id}`, {\n        headers: {\n            'Authorization': `Bearer ${access_token}`\n        }\n    })\n        .then(resp =\u003e {\n            if (resp?.statusText === 'OK') setPedidos(oldState =\u003e\n                oldState.filter(pedido =\u003e\n                    pedido.id !== id));\n        })\n        .catch(err =\u003e console.log(err))\n}\n```\n\n### Encapsulamento e interceptors\n\nPodemos encapsular o Axios em uma constante, de modo a ter como fazer uma chamada com configurações padrões e também criar interceptadores de chamada para envio de dados adicionais.\n\n```ts\n// http/index.ts\n// cria uma instância do axios com algumas configurações comuns\nconst http = axios.create({\n    baseURL: 'http://localhost:8000', // quem usar o http não precisa digitar essa parte da URL\n    headers: {\n        Accept: 'application/json', // na response será aceito somente dados em JSON\n        Content: 'application/json' // no request, iremos sempre enviar dados em JSON\n    }\n})\n\n// agora o post para login pode ser assim\nimport http from \"../../http\";\nhttp.post('public/login', usuario) // não preciso informar a URL completa\n    .then(resp =\u003e {\n        sessionStorage.setItem('token', resp.data.access_token)\n        aoFechar()\n    })\n    .catch(err =\u003e {\n        if (err?.response?.data?.message) alert(err.response.data.message)\n        else (console.log(err))\n    })\n```\n\nO Axios disponibiliza [interceptadores (interceptors)](https://github.com/axios/axios?tab=readme-ov-file#interceptors), que podem ser adicionados à instância criada, para lidar com as requests e responses antes de elas serem enviadas/devolvidas. Por exemplo, na chamada GET para recuperar os pedidos, podemos usar um interceptador de request para passar o token de autenticação e aí então prosseguir com o envio da request. Assim, encapsulamos o token na instância do Axios e ele não precisa mais ser obtido em diferentes lugares do código.\n\n```ts\n// http/index.ts\n// interceptador de requisições (requests)\nhttp.interceptors.request.use(function (config) {\n    // essa função será chamada antes do envio da request\n    // envio do token pelo header da requisição\n    const access_token = sessionStorage.getItem('token');\n    if (access_token \u0026\u0026 config.headers) {\n        config.headers.Authorization = `Bearer ${access_token}`\n    }\n    return config;\n}, function (error) {\n    // essa função será chamada se a request der algum erro\n    console.log('Ocorreu um erro no interceptor do axios!')\n    return Promise.reject(error);\n});\n\n// agora o get de pedidos fica simples e não precisa saber do token:\nconst urlPedidos = 'pedidos';\naxios.get(urlPedidos)\n    .then(resp =\u003e console.log(resp.data))\n    .catch(err =\u003e console.log(err))\n```\n\n### Para saber mais\n\n- Diferença entre autenticação e autorização: https://www.alura.com.br/artigos/autenticacao-autorizacao-seguranca-no-front-end\n\n- Explicação (com códigos) sobre autenticação usando o padrão JWT (JSON Web Tokens): https://www.alura.com.br/artigos/o-que-e-json-web-tokens\n\n    - Vídeo com explicação e parte prática: https://cursos.alura.com.br/extra/alura-mais/o-que-e-json-web-token-jwt--c203\n\n- Modelo de arquitetura REST, alguns de seus princípios e como ele é usado em aplicações Web, aliado com o protocolo HTTP: https://www.alura.com.br/artigos/rest-principios-e-boas-praticas\n\n## Obtenção de dados (data fetching)\n\nExistem alguns padrões que podem ser seguidos para o data fetching:\n\n- standalone: o componente que precisa dos dados é o responsável por fazer a requisição para obtenção desses dados (via fetch, axios, etc.);\n\n- Higher-Order Component (HOC): um \"componente de alta ordem\" nesse caso será o responsável pela obtenção e tratamento dos dados. Ele recebe um componente de entrada, faz o data fetching necessário e retorna novamente o componente recebido, mas enviando via props os dados obtidos. Assim, temos um HOC responsável pelo data fetching e outros componentes responsáveis somente pela UI;\n\n- Hooks customizados: encapsulamos todo o processamento do data fetching em um hook customizado, que retorna esses dados quando utilizado.\n\n### React Query\n\nÉ uma biblioteca famosa que se oferece como alternativa ao data fetching e ao gerenciamento dos estados do servidor.\n\nNecessário instalar. No curso, foi utilizada a versão 4.6.0.\n\n    npm i @tanstack/react-query@4.6.0\n\n**Observação:** a partir da versão 5, algumas funções foram alteradas (o `useQuery` é uma delas). Então as explicações deste README **valem para a versão 4** e podem não estar mais corretas para a versão 5.\n\nDe uma maneira semelhante a como estruturamos o código para uso da Context API, para que componentes possam usar o que o React Query oferece, eles devem ser descendentes de um componente chamado `\u003cQueryClientProvider\u003e`. Este componente requer uma instância da classe `QueryClient`. Exemplo de código:\n\n```ts\n// cliente para efetuar o data fetching\nconst queryClient = new QueryClient();\n\nfunction App() {\n  return (\n    // componente que disponibiliza o React Query para seus componentes-filhos\n    \u003cQueryClientProvider client={queryClient}\u003e\n      \u003cBrowserRouter\u003e\n        \u003cRotas /\u003e\n      \u003c/BrowserRouter\u003e\n    \u003c/QueryClientProvider\u003e\n  );\n}\n```\n\n#### Hook `useQuery`\n\nPara obter dados da API, podemos usar o **hook `useQuery`**, passando dois parâmetros: uma `queryKey` e uma `queryFn`.\n\n- a `queryKey` é um array que contém uma string que você passa para dar um nome único para a query. Caso a função em `queryFn` use variáveis que podem mudar de valor, você passa a variável como próximo elemento no array (é mais fácil entender no código de exemplo a seguir);\n\n- a `queryFn` é uma função que você define para fazer de fato a obtenção dos dados (a \"query\"). Essa função deve retornar uma promise ou um erro.\n\nA `useQuery` retorna uma série de propriedades. Dentre elas estão:\n\n- `data`: retorna os dados da promise, caso tenha sido executada com sucesso;\n\n- `isLoading`: um booleano que informa se a query já terminou;\n\n- `error`: para caso alguma coisa dê errada.\n\nAmbos `data` e `isLoading` são parecidos com variáveis de estado (que você não precisa se preocupar em declarar ou gerenciar), sendo atualizadas pelo próprio `useQuery` e **causam um re-render no componente** quando mudam.\n\nConsulte a [documentação](https://tanstack.com/query/v4/docs/framework/react/reference/useQuery) para mais informação sobre outros parâmetros e propriedades.\n\nVocê pode tipar `data` e `error`. Para isso, use dois generics em `useQuery`, sendo que o primeiro irá tipar `data` e o segundo, `error`. Veja no exemplo:\n\nExemplo de código:\n\n```ts\nconst { slug } = useParams();\n\n// data fetching com React Query\n// no destructuring, posso renomear uma propriedade passando o novo nome após \":\"\nconst { data: categoria, isLoading, error } = useQuery\u003cICategoria, AxiosError\u003e(\n    // queryKey é o primeiro parâmetro e está passando a variável slug como dependência\n    ['categoriaPorSlug', slug], \n    // queryFn é o segundo parâmetro e está chamando uma função definida em outro código\n    () =\u003e obterCategoriaPorSlug(slug || '')\n)\n\nif (error) {\n        console.log(error.message);\n        return \u003ch1\u003eQue vergonha! Alguma coisa deu errado!\u003c/h1\u003e\n    }\n\n// renderiza um ícone de loading enquanto os dados não foram carregados\nif (isLoading) return \u003cLoader /\u003e\n```\n\n## Apollo Client e GraphQL\n\nAssim como o React Query, o [Apollo Client](https://www.apollographql.com/docs/react/get-started) é outra biblioteca que pode ser utilizada para o data fetching e para gerenciamento de estados de dados. Agora, diferente do React Query, o Apollo Client atua em **conjunto com o GraphQL**. \n\nO [**GraphQL**](https://graphql.com/learn/what-is-graphql/) é um tipo de \"query language\" desenvolvida pelo Facebook para interagir com APIs de forma flexível e eficiente. Flexível porque você pode fazer requisições a **diferentes \"endpoints\" e bases de dados** em uma única solicitação, e eficiente porque **você escolhe os dados que quer receber** do back-end, e não tudo de uma vez (ao invés de retornar um JSON completo da base de dados, o GraphQL retorna somente os campos que você pedir, reduzindo o tráfego de rede). Esse é somente um exemplo de algumas das vantagens.\n\n- Ele acaba sendo uma camada intermediária de comunicação entre o Front e o Back-End. O Front diz para ele o que quer receber, e ele se encarrega de ir no Back obter esses dados e devolver somente o que foi pedido.\n\nInstalação das dependências de ambas as tecnologias:\n\n```bash\nnpm install @apollo/client graphql\n```\n\nBem parecido com o visto na [Seção de React Query](#react-query), para fazer as consultas precisamos instanciar um cliente e adicionar um componente provedor para que subcomponentes possam utilizar o Apollo Client e consumir dados da API. Exemplo:\n\n```ts\n// cliente com algumas configurações necessárias\nconst client = new ApolloClient({\n    // endereço para o servidor GraphQL\n    uri: 'http://localhost:9000/graphql', \n    // configuração necessária para informar onde o resultado\n    // das queries será cacheado (armazenado). A classe InMemoryCache \n    // é a comumente utilizada\n    cache: new InMemoryCache(), \n})\n\nfunction App() {\n  return (\n    \u003cApolloProvider client={client}\u003e\n      \u003cBrowserRouter\u003e\n        \u003cRotas /\u003e\n      \u003c/BrowserRouter\u003e\n    \u003c/ApolloProvider\u003e\n  );\n}\n```\n\n### Playground\n\nAo subir o servidor do GraphQL, é disponibilizado na URL http://localhost:9000/graphql uma espécie de \"playground\" em que você pode fazer suas queries e ver o retorno na tela. Há inclusive um autocomplete de campos possíveis de serem pesquisados (comando CTRL + Espaço). Com isso, você pode fazer os testes necessários até chegar no resultado desejado e aí copiar a query para colá-la no código da aplicação de fato.\n\n### Query\n\nUma query é feita solicitando os campos (fields) que você quer de um objeto que a API retorna (as propriedades do objeto). Quando um campo também é um objeto, você tem que informar novamente quais campos você quer desse outro objeto e assim por diante.\n\nPor exemplo, suponha que a API retorne o seguinte no endpoint `/livros`:\n\n```ts\n[\n  {\n    \"id\": 1,\n    \"titulo\": \"Acessibilidade na Web\",\n    \"opcoesCompra\": [\n      {\n        \"id\": 1,\n        \"titulo\": \"E-book\",\n        \"preco\": 29.9,\n      },\n      {\n        \"id\": 2,\n        \"titulo\": \"Impresso\",\n        \"preco\": 39.9\n      },     \n    ]\n  },\n  {\n    \"id\": 2,\n    // ...\n  },\n  //   ...\n]\n```\n\nUma query para obter as propriedades (campos) `id`, `titulo` e `preco` dos livros seria:\n\n```graphql\nlivros {\n  id\n  titulo\n  # opcoesCompra é um objeto, então preciso especificar qual campo eu quero desse objeto\n  opcoesCompra {\n    preco\n  }\n}\n```\n\nO retorno da query é um objeto com uma propriedade `data`. Essa propriedade contém outro objeto, este sim de fato com o conteúdo retornado pela API para os campos solicitados.\n\n```json\n{\n  \"data\": {\n    \"livros\": [\n      {\n        \"id\": 1,\n        \"titulo\": \"Acessibilidade na Web\",\n        \"opcoesCompra\": [\n          {\n            \"preco\": 29.9\n          },\n          {\n            \"preco\": 39.9\n          },\n          {\n            \"preco\": 59.9\n          }\n        ]\n      },\n      // ...\n    ]\n  }\n}\n```\n\n### Mais um hook `useQuery` \n\nNo código, as queries são feitas usando template literals com a função `gql` (essa junção de uma função e template literals é chamado de [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates)). Exemplo:\n\n```ts\nconst OBTER_LIVROS = gql`\n  query ObterLivros {\n    livros {\n      id,\n      imagemCapa,\n      slug,\n      titulo,\n      opcoesCompra {\n        preco,\n        id\n      }\n    }\n  }\n`\n```\n\nPara executar essa query e receber o resultado, usamos o hook `useQuery` (atenção na hora de importar, já que o React Query tem um hook de mesmo nome). Ele espera como parâmetro um tagged template. Exemplo:\n\n```tsx\nconst ListaLivros = ({ categoria }: ListaLivrosProps) =\u003e {\n    // solução com o React Query \n    // const { data: produtos, isLoading } = useQuery(\n    //     ['buscaLivrosPorCategoria', categoria],\n    //     () =\u003e obterProdutosDaCategoria(categoria)\n    // )\n\n    // solução com GraphQL\n    // tipando o campo \"livros\" retornado pelo \"data\" do useQuery\n    const { data } = useQuery\u003c{ livros: ILivro[] }\u003e(OBTER_LIVROS)\n\n    return \u003csection className=\"livros\"\u003e\n        {/* com o React Query */}\n        {/* {produtos?.map(livro =\u003e \u003cMiniCard livro={livro} key={livro.id} /\u003e)} */}\n\n        {/* com GraphQL */}\n        {data?.livros.map(livro =\u003e \u003cMiniCard livro={livro} key={livro.id} /\u003e)}\n    \u003c/section\u003e\n}\n```\n\n- Semelhante ao `useQuery` do React Query, a propriedade `data` causa um **re-render do componente** quando atualizada.\n\n- Também semelhante ao `useQuery` do React Query, temos uma propriedade que retorna um booleano quando a query é finalizada, só que neste caso ela é chamada de `loading` (no hook do React Query é `isLoading`).\n\n### Usando parâmetros\n\nCom o GraphQL, podemos também **criar variáveis e usá-las como argumentos para os campos das queries**, desse modo filtrando quais resultados para um campo queremos que a query traga.\n\nA variável é definida entre parênteses após o nome da query. O nome da variável deve iniciar com `$` e pode ser qualquer nome (ou seja, no exemplo abaixo `$categoriaId` podia ser `$catId` ou qualquer outra coisa). Ela precisa ter um tipo (o GraphQL tem seu próprio conjunto de tipos e também é possível definir novos tipos). Você passa a variável como argumento para algum campo (novamente entre parênteses), associando a variável ao campo que você quer filtrar.\n\nPor exemplo, se queremos obter livros de uma categoria específica, podemos fazer: \n\n```ts\nconst OBTER_LIVROS = gql`\n  query ObterLivros($categoriaId: Int) {\n    livros(categoriaId: $categoriaId) {\n      id,\n      imagemCapa,\n      slug,\n      titulo,\n      opcoesCompra {\n        preco,\n        id\n      }\n    }\n  }\n`\n```\n\nNo `useQuery`, passamos o valor para a variável usando o segundo argumento do hook, por meio de um objeto que tem uma propriedade chamada `variables`:\n\n```ts\nconst { data } = useQuery\u003c{ livros: ILivro[] }\u003e(OBTER_LIVROS, {\n    variables: {\n        categoriaId: categoria.id\n    }\n})\n```\n\n- neste exemplo, o parâmetros é opcional. Quando não enviado à query, ela executa como se não houvesse um filtro. Ou seja, no exemplo acima, caso nenhum parâmetro fosse enviado, `data` retornaria os campos solicitados para os livros de todas as categorias.\n\n- é possível criar parâmetros que sejam obrigatórios. Para isso, adicione `!` após o tipo. Ao tornar um parâmetro obrigatório, caso ele não seja enviado, será retornado um erro. Exemplo de query com parâmetro obrigatório:\n\n```ts\nconst OBTER_LIVROS = gql`\n  query ObterLivros($categoriaId: Int!) {\n    livros(categoriaId: $categoriaId) {\n      id,\n      imagemCapa,\n      slug,\n      titulo,\n      opcoesCompra {\n        preco,\n        id\n      }\n    }\n  }\n`\n```\n\n### `refetch`\n\nO hook `useQuery` também retorna uma função `refetch`. Com ela, é possível reaproveitar a busca do useQuery e fazer uma nova requisição, passando outras variáveis à busca. O resultado da busca será retornado em `data` novamente (sobrescreve o que já tinha em `data`). \n\nPor exemplo, a busca por livros poderia ser por categoria ou por título:\n\n```graphql\n# query no graphQL\nquery ObterLivros($categoriaId: Int, $titulo: String) {\n    livros(categoriaId: $categoriaId, titulo: $titulo) {\n      # ...\n    }\n```\n\nPodemos então usar o `useQuery` uma vez para obter os livros de uma categoria, e então em outro momento usar o `refetch` para filtrar esses livros também pelo título (pense numa busca por título de uma galeria de livros de uma categoria, por exemplo).\n\n```ts\n// busca por uma categoria\nconst { data, refetch } = useQuery\u003c{ livros: ILivro[] }\u003e(OBTER_LIVROS, {\n    variables: {\n      categoriaId: categoria.id\n    }\n  })\n\n// reaproveitando a consulta para buscar também pelo título \nconst buscarLivros = (e: React.FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n  e.preventDefault();\n  if (textoDaBusca) {\n    // o refetch recebe como parâmetro um objeto com variáveis para a query\n    refetch({\n      categoriaId: categoria.id,\n      titulo: textoDaBusca\n    })      \n  }\n}\n```\n\n### Variáveis reativas\n\nÉ uma forma de **gerenciar estados locais**, disponibilizada pelo Apollo Client. Similar ao `useState`, quando um componente usa uma variável reativa (por meio do rook `useReactiveVar`), ele será **re-renderizado caso essa variável reativa seja atualizada**. No entanto, diferente do `useState`, que só pode ser utilizado em componentes, uma variável reativa pode ser utilizada em outras partes da aplicação, e não somente em componentes.\n\nPara criar uma variável reativa, é utilizado o método `makevar`. Ele irá devolver uma **função**, que atua tanto como um getter quanto um setter da variável reativa. Ou seja, para obter o valor de uma variável reativa, você chama a função sem argumentos; já para modificar o valor da variável reativa, você chama a função e passa como argumento o novo valor que você quer atribuir à variável reativa.\n\n- uma convenção é adicionar o sufixo -Var para o nome da  variável reativa.\n\n```ts\n// criando uma variável reativa \nexport const livrosVar = makeVar\u003cILivro[]\u003e([]);\n\n// acessando o valor da variável reativa\nconsole.log(livrosVar());\n\n// setando um novo valor à variável reativa\nlivrosVar(data.livros)\n```\n\nNo caso de componentes, é disponibilizado o hook `useReactiveVar`, com o qual você pode atribuir uma variável reativa a uma variável do componente. Desse modo, será possível tanto usar o valor da variável reativa, quanto fazer com que o componente re-renderize caso a variável reativa seja modificada.\n\n```ts\nconst livros = useReactiveVar(livrosVar)\n```\n\n- se fosse usado `const livros = livrosVar()`, a variável `livros`somente receberia o *valor* de `livrosVar`, mas não se tornaria uma variável de estado (o componente não re-renderizaria caso `livrosVar` fosse modificado).\n\n#### Unindo variáveis reativas com o `useQuery`\n\nPodemos atualizar o valor de uma variável reativa usando outra opção disponível no segundo parâmetro do `useQuery`: a função callback `onCompleted`. Essa função é chamada quando a query é finalizada com sucesso.\n\n```ts\nexport const useLivros = (categoria: ICategoria) =\u003e {\n    // tipando o campo \"livros\" retornado pelo \"data\" do useQuery\n    return useQuery\u003c{ livros: ILivro[] }\u003e(OBTER_LIVROS, {\n        variables: {\n            categoriaId: categoria.id\n        },\n        // atualizando a variável reativa com o resultado da query\n        onCompleted(data) {\n            if (data.livros) livrosVar(data.livros);\n        }\n    })\n}\n```\n\nCom isso, podemos encapsular toda a parte de consulta em um código à parte, que chama a `useQuery` e atualiza o estado da variável reativa, e então usar somente a variável reativa no componente por meio do hook `useReactiveVar`, separando as responsabilidades.\n\n### Mutations\n\nMutation é a forma de adicionar/atualizar dados no GraphQL. As mutations que estão disponíveis para uso são listadas na aba \"DOCS\" do playground (imagino que a pessoa responsável pelo back-end cria essas mutations). \n\nPara usar uma mutation no GraphQL, usamos a palavra-chave `mutation`, damos um nome a ela, e então adicionamos a mutation que queremos usar, passando os parâmetros se necessário.\n\n```graphql\nmutation MinhaMutation($id: Int!) {\n  nomeDaMutation(id: $id)\n}\n```\n\n#### `useMutation`\n\nEste é o hook que utilizamos para executar uma mutation. Ele devolve uma tupla, sendo que o primeiro elemento é a função que executa a mutation no graphQL (podemos dar o nome que quisermos a essa função). O segundo elemento é um objeto com os resultados da execução da mutation; dentre eles, temos a propriedade booleana `loading`, que é true se a query ainda estão em execução. \n\nBasta então executar a função retornada pelo `useMutation` e, caso a mutation precise de algum parâmetro, enviamos à função em um objeto com a propriedade `variables`. \n\n```ts\nconst ADICIONAR_ITEM = gql`\nmutation AdicionarItem($item: ItemCarrinhoInput!) {\n  adicionarItem(item: $item)\n}\n`\n\nconst [nomeParaAFuncao, { loading }] = useMutation(ADICIONAR_ITEM);\n\nconst adicionarItemCarrinho = (item: IItemCarrinho) =\u003e {\n    nomeParaAFuncao({\n        variables: {\n            item: {\n                livroId: item.livroId,\n                opcaoCompraId: item.opcaoCompra.id,\n                quantidade: item.quantidade\n            }\n        }\n    })\n}\n```\n\n##### `refetchQueries`\n\nO `useMutation` aceita um segundo parâmetro, que é um objeto com opções. Uma dessas opções é a propriedade chamada `refetchQueries`. Ela é um array em que você pode passar as queries que deseja que sejam executadas novamente após uma mutation. Por exemplo, após uma mutation que modifica a quantidade de um item do carrinho, você pode solicitar o refetch da query que obtém dados do carrinho, de modo a receber os itens e valores atualizados.\n\n  - esse array aceita tanto uma string com o nome de uma query que já foi executada (query nomeada dentro de um gql) quanto uma variável que tenha a template literals com a função `gql` da query.\n\n```ts\nconst adicionarAoCarrinho = useMutation(ADICIONAR_ITEM, {\n    // faço novamente a query de obter carrinho toda vez que a função de mutation for \n    // chamada, de modo a atualizar o carrinho. OBTER_CARRINHO é uma variável que contém\n    // a template literals com a função gql que executa uma query chamada obterCarrinho\n    refetchQueries: [OBTER_CARRINHO]\n});\n```\n\n## Dicas extras\n\n- O React possui a biblioteca `Intl` que auxilia na internacionalização de alguns dados, devolvendo-os formatado adequadamente à localização da pessoa usuária. Por exemplo, para devolver um número no formato da moeda brasileira, podemos criar uma função formatadora:\n\n```ts\nconst formataMoeda = Intl.NumberFormat('pt-br', { style: 'currency', currency:'BRL' });\nconsole.log(formataMoeda.format(126.9)); // R$ 126,90\n```\n\n- Imprimir datas usando o `Date` do JavaScript às vezes pode causar efeitos inesperados como a data do dia anterior sendo impressa (devido a questões de fuso horário). Uma forma de imprimir a data correta é fazer uma função formatadora que leve em conta o fuso do computador em que a aplicação estiver rodando:\n\n```ts\nconst formataData = (data: Date) =\u003e {\n    const timezoneOffset = data.getTimezoneOffset()\n    data.setMinutes(data.getMinutes() + timezoneOffset) // ajuste do tempo para a máquina rodando o app\n    return data.toLocaleDateString()\n}\nconsole.log(formataData(new Date(\"2022-08-01\"))) // 01/08/2022\nconsole.log(new Date(\"2022-08-01\").toLocaleDateString()) // 31/07/2022\n```\n\n- O site [Loading.io](https://loading.io/css) disponibiliza 12 ícones diferentes de loading feitos puramente com CSS. Você pode selecionar o que deseja e copiar o HTML/CSS para renderizá-lo em sua página. Esses ícones estão gratuitos, sob a [licença CC0](https://creativecommons.org/public-domain/cc0/).\n\n## Instalação\n\nO projeto foi criado com o Create React App, utilizando Node.js e npm. É necessário estar com ambos instalados em sua máquina para rodar a aplicação.\n\nApós clonar/baixar o projeto, abra um terminal, navegue até a pasta do projeto e rode o seguinte comando para instalar todas as dependências necessárias:\n\n    npm install\n\nApós isso, você pode rodar a aplicação em modo de desenvolvimento com o seguinte comando:\n\n    npm start\n\nA aplicação irá rodar no endereço http://localhost:3000.\n\n### Primeira API \n\nPara a parte de autenticação e autorização, bem como para recuperar dados de livros, é necessário instalar a API que irá rodar localmente. Após o download/clone do projeto [neste repositório](https://github.com/viniciosneves/api-alurabooks), rode os comandos abaixo:\n\n    npm install\n    npm run start-auth\n\nA API irá rodar no endereço http://localhost:8000.\n\n### Segunda API\n\nO curso de GraphQL incluiu uma segunda API mockada, contando também com um Apollo Server. O projeto pode ser baixado/clonado [deste repositório](https://github.com/alura-cursos/alurabooks-gql). Para instalação o comando é o mesmo:\n\n```bash\nnpm install\n```\n\nNesta segunda API, precisaremos de dois terminais, pois iremos rodar dois serviços (a API e o GraphQL). No primeiro terminal, digite o seguinte comando para subir o servidor do GraphQL:\n\n```bash\n$ npm run start:dev\n```\n\nO GraphQL irá rodar no endereço: http://localhost:9000/graphql\n\nNo segundo terminal, execute o comando abaixo para subir a API mockada de fato (onde estão os dados):\n\n```bash\n$ npm run start:api\n```\n\nA API irá rodar no endereço http://localhost:8000. Observe que é o **mesmo endereço** da primeira API, ou seja, rode somente uma delas para fazer os testes. \n\n- ambas APIs mockadas têm a mesma estrutura de base de dados (a segunda contém mais dados). Tanto faz qual você subir, a aplicação irá funcionar. As únicas exceções são os usuários que você tenha cadastrado via modal de cadastro do site, ou livros que tenha deletado da página de pedidos. Nestes casos, essas mudanças serão refletidas somente na base de dados da API que estiver rodando no momento das suas ações.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzingarelli%2Falura-books-typescript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzingarelli%2Falura-books-typescript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzingarelli%2Falura-books-typescript/lists"}