Leodario.com

Leodario.com – Tudo sobre Tecnologia

Aula 4 – API JavaScript, Node.js e Express – REST vs SOAP vs GraphQL – Tipos de APIs

Imagem destacada da aula de API

Introdução

Prezados alunos e alunas, bem-vindos à quarta aula do nosso curso! Eu sou seu professor PHD em APIs e hoje mergulharemos em um tópico decisivo para qualquer desenvolvedor moderno: os diferentes tipos de APIs. No vasto universo da conectividade de software, escolher o tipo certo de API é tão fundamental quanto escolher a ferramenta correta para um carpinteiro.

Imagine que você está organizando um evento e precisa encomendar comida. Você tem várias opções:

    • Ligar para uma pizzaria e pedir uma pizza específica (e talvez só consiga a pizza inteira, mesmo querendo apenas dois pedaços).
    • Usar um aplicativo de entrega onde você seleciona exatamente os pratos e ingredientes que deseja de diversos restaurantes.
    • Contratar um serviço de catering que segue um contrato formal e detalhado, com um cardápio fixo e regras estritas para cada entrega.

Cada uma dessas opções representa uma maneira diferente de “falar” com o serviço de comida. No mundo das APIs, temos analogias semelhantes, e hoje vamos desvendar as três principais: REST, SOAP e GraphQL.

Por que isso é tão significativo para APIs modernas? Porque a escolha do tipo de API influencia diretamente a performance, a flexibilidade, a segurança, a complexidade de desenvolvimento e a escalabilidade da sua aplicação. Compreender essas diferenças possibilita que você desenvolva soluções mais robustas, eficientes e adequadas aos requisitos específicos de cada projeto.

Nesta aula, você vai entender exatamente o que são REST, SOAP e GraphQL, quais são suas características distintivas, suas vantagens e desvantagens, e quando cada um deles se mostra a opção mais valiosa. Vamos desmistificar a terminologia e apresentar casos de uso reais que solidificarão seu aprendizado.

Em nosso ecossistema Node.js e Express, o estilo REST é o mais disseminado e intuitivo, sendo o ponto de partida para muitos. No entanto, o GraphQL vem ganhando imensa popularidade por sua flexibilidade, e você pode encontrar sistemas legados que ainda dependem de SOAP. Dominar esses conceitos não apenas amplia seu repertório técnico, mas também o habilita a tomar decisões de arquitetura mais inteligentes e estratégicas.

Conceito Fundamental

Vamos agora aprofundar nossa compreensão sobre cada um desses padrões de comunicação.

REST (Representational State Transfer)

O REST não é um protocolo, mas sim um estilo arquitetural para sistemas distribuídos. Ele é o mais comum e amplamente utilizado na web atualmente, sendo a base da maioria das APIs públicas.

    • Explicação detalhada: APIs RESTful são construídas em cima do protocolo HTTP e utilizam os métodos HTTP padrão (GET para obter, POST para criar, PUT para atualizar completamente, PATCH para atualizar parcialmente e DELETE para remover) para manipular recursos. Cada recurso é identificado por uma URI (Uniform Resource Identifier) única, como /usuarios/123. Uma característica essencial do REST é ser stateless (sem estado), o que significa que cada requisição do cliente para o servidor deve conter todas as informações necessárias para que o servidor a compreenda, sem depender de contexto de requisições anteriores.
    • Terminologia correta:Recursos, stateless (sem estado), idemponentes (múltiplas chamadas ao mesmo método com os mesmos parâmetros têm o mesmo efeito que uma única chamada, como DELETE ou PUT), verbs HTTP (verbos HTTP), URI.
    • Casos de uso reais: Praticamente todas as APIs de redes sociais (Facebook Graph API para RESTful HTTP), e-commerce (Amazon, Mercado Livre), aplicativos móveis e a maioria dos serviços web modernos utilizam REST.
    • Como isso se integra com outras tecnologias: Integra-se perfeitamente com navegadores web, frameworks front-end (React, Angular, Vue), aplicativos móveis e outras APIs. Usa formatos de dados como JSON (mais comum) e XML.
    • Vantagens:
      • Simplicidade: Fácil de entender e desenvolver, utilizando padrões web existentes.
      • Escalabilidade: O modelo stateless facilita a distribuição de requisições entre múltiplos servidores.
      • Cache: Suporte nativo para caching via HTTP, o que pode melhorar significativamente a performance.
      • Flexibilidade: Permite diversos formatos de dados.
    • Desvantagens:
      • Over-fetching/Under-fetching: O cliente frequentemente recebe mais dados do que precisa (over-fetching) ou precisa fazer múltiplas requisições para obter todos os dados necessários (under-fetching).
      • Ausência de tipagem forte: Pode levar a erros em tempo de execução se os dados não forem validados corretamente.

SOAP (Simple Object Access Protocol)

O SOAP é um protocolo formal e mais rígido, geralmente utilizado em ambientes corporativos e legados, onde a segurança e a confiabilidade são prioridade máxima.

    • Explicação detalhada: SOAP é um protocolo de mensagens baseado em XML. Ele define uma estrutura de envelope XML para o envio de mensagens, que inclui um cabeçalho (opcional, para metadados) e um corpo (conteúdo da mensagem). A descrição dos serviços SOAP é feita através de um arquivo WSDL (Web Services Description Language), que atua como um “contrato” detalhado sobre as operações disponíveis, seus parâmetros e tipos de retorno. SOAP é transport-agnostic, o que significa que pode operar sobre HTTP, SMTP, TCP, entre outros, embora HTTP seja o mais comum.
    • Terminologia correta:WSDL (Web Services Description Language), XML, Envelope, Header, Body, Fault.
    • Casos de uso reais: Serviços bancários, sistemas financeiros, integração com ERPs (SAP), aplicações governamentais e sistemas de grande escala onde a interoperabilidade rigorosa e a segurança são essenciais.
    • Como isso se integra com outras tecnologias: Frequentemente usado com linguagens como Java e .NET, e requer bibliotecas específicas para parsing de XML e geração de proxies de cliente.
    • Vantagens:
      • Segurança robusta: Suporte nativo para WS-Security, permitindo criptografia, assinaturas digitais e autenticação.
      • ACID transactions: Pode suportar transações ACID (Atomicidade, Consistência, Isolamento, Durabilidade), crucial para sistemas financeiros.
      • Mensagens confiáveis: Suporte a WS-ReliableMessaging para garantir a entrega de mensagens.
      • Tipagem forte: O WSDL impõe um contrato rigoroso, minimizando erros de integração.
    • Desvantagens:
      • Complexidade: Mais complexo de desenvolver e configurar, com mais “boilerplate” (código repetitivo).
      • Verbosity: O uso de XML torna as mensagens mais longas e menos legíveis do que JSON.
      • Performance: Geralmente mais lento devido ao parsing de XML e à sobrecarga do protocolo.
      • Curva de aprendizado: Mais íngreme para desenvolvedores iniciantes.

GraphQL

O GraphQL é uma linguagem de consulta para APIs e um runtime para executar essas consultas, desenvolvido pelo Facebook. Ele oferece uma abordagem mais flexível e eficiente para a busca de dados.

    • Explicação detalhada: Ao contrário de REST, que possui múltiplos endpoints para diferentes recursos, uma API GraphQL expõe um único endpoint. O cliente envia uma consulta (query) descrevendo exatamente quais dados precisa, e o servidor responde com apenas esses dados. Isso elimina os problemas de over-fetching e under-fetching. Ele é fortemente tipado, o que significa que você define um schema (esquema) com todos os tipos de dados e operações disponíveis. As operações podem ser Queries (para leitura), Mutations (para escrita/modificação) e Subscriptions (para atualizações em tempo real).
    • Terminologia correta:Schema, Type, Query, Mutation, Resolver, Subscription.
    • Casos de uso reais: Aplicativos móveis complexos, dashboards com muitas fontes de dados, microserviços, front-ends que precisam de dados altamente customizados (e.g., o próprio Facebook, GitHub API).
    • Como isso se integra com outras tecnologias: Integra-se de forma excepcional com frameworks front-end (React, Vue, Angular) através de clientes como Apollo Client ou Relay. Pode agregar dados de múltiplas fontes (bancos de dados, APIs REST, outros serviços) em uma única requisição.
    • Vantagens:
      • Eficiência na busca de dados: O cliente solicita apenas o que precisa, reduzindo a quantidade de dados transferidos e o número de requisições.
      • Tipagem forte e introspecção: O schema garante que os dados sejam estruturados e consistentes, e a introspecção facilita a exploração da API.
      • Evolução simplificada: Adicionar novos campos não afeta clientes existentes.
      • Atualizações em tempo real: Subscriptions viabilizam funcionalidades de tempo real facilmente.
    • Desvantagens:
      • Complexidade de caching: Caching com GraphQL pode ser mais desafiador do que com REST, dada a flexibilidade das queries.
      • Upload de arquivos: Upload de arquivos pode ser um pouco mais complexo de implementar diretamente.
      • Curva de aprendizado: Requer um entendimento de schema, resolvers e uma nova sintaxe de query.
      • N+1 problem: Se não for bem implementado (com dataloaders), pode levar a múltiplas consultas ao banco de dados para um único campo.

A escolha entre REST, SOAP e GraphQL é uma decisão arquitetural crítica. Enquanto REST brilha pela simplicidade e ubiquidade, SOAP oferece robustez e segurança para cenários corporativos, e GraphQL revoluciona a flexibilidade na busca de dados.

Implementação Prática

Nesta seção, vamos construir uma aplicação Node.js com Express que demonstra o estilo REST. Em seguida, faremos uma integração simplificada e conceitual de GraphQL para você ver como ele se encaixa. Para SOAP, devido à sua complexidade e foco em consumo de serviços (ao invés de criação simples para iniciantes), demonstraremos como um aplicativo Express consumiria um serviço SOAP externo, em vez de criar um serviço SOAP completo.

Para garantir compatibilidade com ambientes como o HostGator Plano M, vamos usar a porta definida pela variável de ambiente PORT ou padrão 3000.

Primeiro, crie um novo diretório e inicialize seu projeto Node.js:

mkdir api-types-lesson
cd api-types-lesson
npm init -y
npm install express apollo-server-express graphql body-parser axios soap dotenv

Agora, crie um arquivo index.js na raiz do projeto.


// Carrega variáveis de ambiente do arquivo .env
// Isso é crucial para configurações sensíveis e portabilidade em hosts como HostGator
require('dotenv').config();

// Importa o framework Express para construir a API REST const express = require('express'); // Importa o módulo body-parser para lidar com o corpo das requisições POST/PUT const bodyParser = require('body-parser'); // Importa axios para fazer requisições HTTP (útil para consumir APIs externas, como SOAP) const axios = require('axios'); // Importa o cliente SOAP para Node.js const soap = require('soap'); // Importa módulos do Apollo Server para integrar GraphQL const { ApolloServer, gql } = require('apollo-server-express');

// Inicializa o aplicativo Express const app = express(); // Define a porta, pegando da variável de ambiente PORT ou usando 3000 como padrão const PORT = process.env.PORT || 3000;

// Configura o Express para usar body-parser para JSON // Isso habilita o app a ler corpos de requisição no formato JSON app.use(bodyParser.json()); // Opcional: para codificação de URL, útil em formulários HTML app.use(bodyParser.urlencoded({ extended: true }));

// --- REST API (Demonstração) ---

// Base de dados em memória simples para a demonstração REST // Em uma aplicação real, isso seria um banco de dados (MongoDB, PostgreSQL, etc.) let users = [ { id: '1', name: 'Alice', email: '[email protected]' }, { id: '2', name: 'Bob', email: '[email protected]' }, ];

/* @route GET /api/users @description Retorna a lista de todos os usuários @access Public / app.get('/api/users', (req, res) => { try { // Loga a requisição para fins de depuração e auditoria console.log([${new Date().toISOString()}] Requisição GET em /api/users); // Envia a lista de usuários como resposta JSON res.status(200).json(users); } catch (error) { // Tratamento de erro básico console.error(Erro ao buscar usuários: ${error.message}); // Retorna um erro 500 (Internal Server Error) res.status(500).json({ message: 'Erro interno do servidor ao buscar usuários.' }); } });

/ @route GET /api/users/:id @description Retorna um usuário específico pelo ID @access Public / app.get('/api/users/:id', (req, res) => { try { const { id } = req.params; // Extrai o ID da URL // Loga a requisição com o ID console.log([${new Date().toISOString()}] Requisição GET em /api/users/${id}); // Busca o usuário na base de dados em memória const user = users.find(u => u.id === id);

if (!user) { // Se o usuário não for encontrado, retorna 404 (Not Found) console.warn(Usuário com ID ${id} não encontrado.); return res.status(404).json({ message: 'Usuário não encontrado.' }); } // Retorna o usuário encontrado res.status(200).json(user); } catch (error) { console.error(Erro ao buscar usuário ${req.params.id}: ${error.message}); res.status(500).json({ message: 'Erro interno do servidor ao buscar usuário.' }); } });

/ @route POST /api/users @description Cria um novo usuário @access Public @body { name: string, email: string } / app.post('/api/users', (req, res) => { try { const { name, email } = req.body; // Extrai name e email do corpo da requisição console.log([${new Date().toISOString()}] Requisição POST em /api/users com dados:, req.body);

// Validação de entrada robusta: garante que name e email existam if (!name || !email) { console.warn('Tentativa de criar usuário sem nome ou email.'); return res.status(400).json({ message: 'Nome e email são obrigatórios.' }); }

// Gera um ID simples (em produção, usaria um UUID ou ID do DB) const newId = (users.length + 1).toString(); const newUser = { id: newId, name, email }; users.push(newUser); // Adiciona o novo usuário à base de dados

// Retorna o novo usuário com status 201 (Created) res.status(201).json(newUser); } catch (error) { console.error(Erro ao criar usuário: ${error.message}); res.status(500).json({ message: 'Erro interno do servidor ao criar usuário.' }); } });

// --- GraphQL (Demonstração Conceitual Simplificada) --- // Para demonstrar o GraphQL, vamos criar um schema e um resolver mínimos. // Em um projeto real, o schema seria mais complexo e os resolvers interagiram com um DB.

// 1. Definindo o Schema GraphQL // Usamos a sintaxe de linguagem de schema GraphQL (SDL) const typeDefs = gql type User { id: ID! name: String! email: String! }

type Query { hello: String allUsers: [User] user(id: ID!): User }

type Mutation { createUser(name: String!, email: String!): User } ;

// 2. Definindo os Resolvers // Resolvers são funções que 'resolvem' os dados para um campo no schema. const resolvers = { Query: { hello: () => 'Olá do GraphQL!', allUsers: () => users, // Retorna nossa lista de usuários REST user: (parent, { id }) => users.find(u => u.id === id), }, Mutation: { createUser: (parent, { name, email }) => { if (!name || !email) { // Em GraphQL, erros de validação são normalmente tratados com // exceções que o Apollo Server formata. throw new Error('Nome e email são obrigatórios para criar um usuário.'); } const newId = (users.length + 1).toString(); const newUser = { id: newId, name, email }; users.push(newUser); return newUser; }, }, };

// 3. Inicializando o Apollo Server const server = new ApolloServer({ typeDefs, resolvers });

// 4. Aplicando o middleware do Apollo Server ao Express // Este bloco 'async' é necessário para iniciar o servidor Apollo. async function startApolloServer() { await server.start(); // Inicia o servidor GraphQL // Aplica o middleware GraphQL no endpoint /graphql app.use('/graphql', bodyParser.json(), express.json(), server.getMiddleware({ path: '/graphql' })); console.log([${new Date().toISOString()}] GraphQL Server pronto em http://localhost:${PORT}/graphql); } startApolloServer(); // Chama a função para iniciar o servidor GraphQL

// --- SOAP (Demonstração de Consumo) --- // Para SOAP, ao invés de criar um serviço, mostraremos como consumir um serviço SOAP externo. // Isso é mais comum em Node.js, onde você se conecta a sistemas legados.

// Exemplo de URL WSDL de um serviço SOAP público (converter Celsius para Fahrenheit) const wsdlUrl = 'http://www.dneonline.com/calculator.asmx?wsdl';

/* @route POST /api/convert-celsius-to-fahrenheit @description Consome um serviço SOAP para converter temperatura @access Public @body { celsius: number } / app.post('/api/convert-celsius-to-fahrenheit', async (req, res) => { try { const { celsius } = req.body; console.log([${new Date().toISOString()}] Requisição POST em /api/convert-celsius-to-fahrenheit para ${celsius}°C);

// Validação simples da entrada if (typeof celsius !== 'number') { return res.status(400).json({ message: 'O valor de celsius deve ser um número.' }); }

// Cria o cliente SOAP. O callback é importante aqui. soap.createClient(wsdlUrl, (err, client) => { if (err) { console.error(Erro ao criar cliente SOAP: ${err.message}); return res.status(500).json({ message: 'Falha ao conectar ao serviço SOAP externo.' }); }

// Chama a operação SOAP "CelsiusToFahrenheit" client.CelsiusToFahrenheit({ Celsius: celsius }, (err, result) => { if (err) { console.error(Erro na chamada SOAP CelsiusToFahrenheit: ${err.message}); return res.status(500).json({ message: 'Erro ao converter temperatura via SOAP.' }); } // O resultado vem aninhado, precisamos extrair o valor const fahrenheit = result.CelsiusToFahrenheitResult; console.log(Conversão SOAP: ${celsius}°C = ${fahrenheit}°F); res.status(200).json({ celsius, fahrenheit }); }); });

} catch (error) { console.error(Erro na rota de conversão SOAP: ${error.message}); res.status(500).json({ message: 'Erro interno do servidor ao processar conversão SOAP.' }); } });

// --- Tratamento de erros globais (Melhores Práticas Enterprise) ---

// Middleware para lidar com rotas não encontradas (404) app.use((req, res, next) => { console.warn([${new Date().toISOString()}] Rota não encontrada: ${req.method} ${req.originalUrl}); res.status(404).json({ message: 'Recurso não encontrado. Verifique a URL e o método da requisição.' }); });

// Middleware genérico para tratamento de erros (500) app.use((err, req, res, next) => { console.error([${new Date().toISOString()}] Erro interno do servidor: ${err.stack}); res.status(500).json({ message: 'Ocorreu um erro inesperado no servidor. Tente novamente mais tarde.' }); });

// Inicia o servidor Express na porta definida app.listen(PORT, () => { console.log([${new Date().toISOString()}] Servidor Express rodando na porta ${PORT}); console.log([${new Date().toISOString()}] API REST em http://localhost:${PORT}/api/users); console.log([${new Date().toISOString()}] Utilize o playground GraphQL em http://localhost:${PORT}/graphql); console.log([${new Date().toISOString()}] Exemplo de consumo SOAP em http://localhost:${PORT}/api/convert-celsius-to-fahrenheit); });

Para o arquivo .env, crie um arquivo chamado .env na raiz do projeto com o seguinte conteúdo (opcional, mas uma boa prática):


PORT=3000

Configurações Específicas para HostGator Plano M:

    • O HostGator Plano M geralmente executa aplicativos Node.js usando o comando npm start. Certifique-se de que seu package.json tenha um script start apontando para seu arquivo principal (ex: "start": "node index.js").
    • A variável de ambiente PORT é tipicamente injetada pelo ambiente do host. Nosso código process.env.PORT || 3000 garante que ele use a porta fornecida pelo HostGator ou a padrão 3000 se não for especificada, o que é uma prática enterprise para portabilidade.
    • Certifique-se de que todos os módulos (express, body-parser, apollo-server-express, graphql, axios, soap, dotenv) estejam listados nas dependências do seu package.json para que o HostGator os instale.
    • Nossa base de dados em memória users é para demonstração. Em produção, você integraria um banco de dados externo compatível com HostGator ou acessível via sua rede.
    • O logging via console.log é básico. Para ambientes de produção, uma solução como Winston ou Pino seria altamente recomendada para um controle mais granular e persistência dos logs.

Como testar:

Para iniciar a aplicação, execute no terminal:


node index.js

Você verá as mensagens de inicialização no console.

Testando a API REST:

    • GET http://localhost:3000/api/users: Retorna todos os usuários.
    • GET http://localhost:3000/api/users/1: Retorna o usuário com ID 1.
    • POST http://localhost:3000/api/users: Cria um novo usuário. Use um cliente como Postman, Insomnia ou curl com um corpo JSON:
      
      curl -X POST -H "Content-Type: application/json" -d '{"name": "Charlie", "email": "[email protected]"}' http://localhost:3000/api/users
              

Testando a API GraphQL:

    • Acesse http://localhost:3000/graphql no navegador. Você será redirecionado para o playground do Apollo Server, onde pode executar queries:
      
      query {
        allUsers {
          id
          name
        }
        user(id: "1") {
          email
        }
        hello
      }
              

      Ou uma mutation:

      
      mutation {
        createUser(name: "David", email: "[email protected]") {
          id
          name
          email
        }
      }
              

Testando o Consumo SOAP:

    • POST http://localhost:3000/api/convert-celsius-to-fahrenheit: Consome o serviço SOAP externo. Use um cliente HTTP com um corpo JSON:
      
      curl -X POST -H "Content-Type: application/json" -d '{"celsius": 25}' http://localhost:3000/api/convert-celsius-to-fahrenheit
              

      A resposta esperada seria: {"celsius":25,"fahrenheit":"77"}

Este código funcional e completo oferece uma visão prática das diferentes abordagens. O tratamento de erros é básico, mas a estrutura com try/catch em cada rota e middlewares de erro genéricos são padrões enterprise para garantir a robustez da aplicação.

Exercício Hands-On

Agora é a sua vez de colocar a mão na massa! O desafio prático vai solidificar o que aprendemos sobre REST e GraphQL.

Desafio:

Adicione uma nova funcionalidade à nossa aplicação: gerenciar uma lista de produtos.

    • Para a API REST:
      • Crie uma nova base de dados em memória para products (ID, name, price).
      • Implemente uma rota GET /api/products para listar todos os produtos.
      • Implemente uma rota GET /api/products/:id para obter um produto específico.
      • Implemente uma rota POST /api/products para adicionar um novo produto. Inclua validação básica para name e price.
    • Para a API GraphQL:
      • Adicione um novo tipo Product ao seu typeDefs (com os campos id, name, price).
      • Adicione uma nova query allProducts que retorne uma lista de [Product].
      • Adicione uma nova query product(id: ID!): Product para buscar um produto pelo ID.
      • Adicione uma nova mutation createProduct(name: String!, price: Float!): Product para criar um produto.
      • Implemente os resolvers correspondentes para essas novas queries e mutation.

Solução detalhada passo a passo:

Vamos modificar o arquivo index.js.

    • Adicione a base de dados de produtos em memória:

      Após a variável users, adicione:

      
      let products = [
          { id: '1', name: 'Laptop', price: 1200.00 },
          { id: '2', name: 'Mouse', price: 25.50 },
      ];
      

    • Implemente as rotas REST para produtos:

      Adicione estas rotas após as rotas de usuários:

      
      // --- Rotas REST para Produtos ---
      app.get('/api/products', (req, res) => {
          try {
              console.log([${new Date().toISOString()}] Requisição GET em /api/products);
              res.status(200).json(products);
          } catch (error) {
              console.error(Erro ao buscar produtos: ${error.message});
              res.status(500).json({ message: 'Erro interno do servidor ao buscar produtos.' });
          }
      });

      app.get('/api/products/:id', (req, res) => { try { const { id } = req.params; console.log([${new Date().toISOString()}] Requisição GET em /api/products/${id}); const product = products.find(p => p.id === id); if (!product) { console.warn(Produto com ID ${id} não encontrado.); return res.status(404).json({ message: 'Produto não encontrado.' }); } res.status(200).json(product); } catch (error) { console.error(Erro ao buscar produto ${req.params.id}: ${error.message}); res.status(500).json({ message: 'Erro interno do servidor ao buscar produto.' }); } });

      app.post('/api/products', (req, res) => { try { const { name, price } = req.body; console.log([${new Date().toISOString()}] Requisição POST em /api/products com dados:, req.body);

      if (!name || typeof price !== 'number' || price <= 0) { console.warn('Tentativa de criar produto sem nome ou preço inválido.'); return res.status(400).json({ message: 'Nome e preço (número positivo) são obrigatórios.' }); }

      const newId = (products.length + 1).toString(); const newProduct = { id: newId, name, price }; products.push(newProduct);

      res.status(201).json(newProduct); } catch (error) { console.error(Erro ao criar produto: ${error.message}); res.status(500).json({ message: 'Erro interno do servidor ao criar produto.' }); } });

    • Adicione o tipo Product e as queries/mutations GraphQL:

      Modifique typeDefs para incluir o novo tipo e operações:

      
      const typeDefs = gql
          type User {
              id: ID!
              name: String!
              email: String!
          }

      type Product { id: ID! name: String! price: Float! }

      type Query { hello: String allUsers: [User] user(id: ID!): User allProducts: [Product] # Nova query para produtos product(id: ID!): Product # Nova query para produto por ID }

      type Mutation { createUser(name: String!, email: String!): User createProduct(name: String!, price: Float!): Product # Nova mutation para produtos } ;

    • Implemente os resolvers para produtos:

      Modifique resolvers para incluir a lógica para os novos campos:

      
      const resolvers = {
          Query: {
              hello: () => 'Olá do GraphQL!',
              allUsers: () => users,
              user: (parent, { id }) => users.find(u => u.id === id),
              allProducts: () => products, // Resolve para a lista de produtos
              product: (parent, { id }) => products.find(p => p.id === id), // Resolve produto por ID
          },
          Mutation: {
              createUser: (parent, { name, email }) => {
                  if (!name || !email) {
                      throw new Error('Nome e email são obrigatórios para criar um usuário.');
                  }
                  const newId = (users.length + 1).toString();
                  const newUser = { id: newId, name, email };
                  users.push(newUser);
                  return newUser;
              },
              createProduct: (parent, { name, price }) => { // Novo resolver para criar produto
                  if (!name || typeof price !== 'number' || price <= 0) {
                      throw new Error('Nome e preço (número positivo) são obrigatórios para criar um produto.');
                  }
                  const newId = (products.length + 1).toString();
                  const newProduct = { id: newId, name, price };
                  products.push(newProduct);
                  return newProduct;
              },
          },
      };
      

Como testar e validar o resultado:

Reinicie seu servidor Node.js (Ctrl+C e node index.js).

    • Validar REST de Produtos:
      • GET http://localhost:3000/api/products: Deve retornar sua lista de produtos.
      • GET http://localhost:3000/api/products/1: Deve retornar o laptop.
      • POST http://localhost:3000/api/products:
        
        curl -X POST -H "Content-Type: application/json" -d '{"name": "Teclado", "price": 75.99}' http://localhost:3000/api/products
                        

        Verifique se o novo produto aparece ao fazer um GET /api/products.

    • Validar GraphQL de Produtos:
      • Acesse http://localhost:3000/graphql. No playground, execute a seguinte query:
        
        query {
          allProducts {
            id
            name
            price
          }
          product(id: "1") {
            name
            price
          }
        }
                        

      • Execute a mutation para criar um produto:
        
        mutation {
          createProduct(name: "Monitor", price: 299.99) {
            id
            name
            price
          }
        }
                        

        Verifique novamente com a query allProducts.

Troubleshooting dos erros mais comuns:

    • "Address already in use": Significa que a porta 3000 (ou a que você definiu) já está sendo usada. Certifique-se de que a instância anterior do seu servidor foi encerrada (Ctrl+C).
    • "Cannot GET /api/products": Verifique se a URL está correta e se o método HTTP é GET. Garanta que a rota foi adicionada corretamente no seu index.js.
    • Erros de JSON ou corpo vazio em POST: Certifique-se de que está enviando Content-Type: application/json no cabeçalho e que o corpo da sua requisição POST é um JSON válido.
    • Erros no GraphQL Playground: Verifique a sintaxe da sua query ou mutation. O playground geralmente aponta erros de sintaxe. Se os dados não retornam, confira se os nomes dos campos no seu typeDefs e resolvers correspondem.
    • Erro ao conectar ao serviço SOAP: Verifique sua conexão com a internet. O serviço dneonline.com é externo e pode estar temporariamente indisponível.

Próximos passos sugeridos:

    • Para REST: Explore o uso de bancos de dados reais (MongoDB com Mongoose, PostgreSQL com Sequelize) para persistir seus dados. Implemente as operações PUT e DELETE para os recursos users e products. Adicione autenticação e autorização às suas rotas.
    • Para GraphQL: Aprofunde-se nos conceitos de DataLoader para resolver o problema N+1, otimizando o carregamento de dados. Explore as Subscriptions para implementar funcionalidades em tempo real. Pense em como você integraria o GraphQL com um banco de dados.
    • Para SOAP: Embora menos comum para novos serviços em Node.js, aprenda a gerar classes cliente a partir de um WSDL para automatizar o consumo de serviços SOAP complexos.
    • Considere adicionar uma camada de serviço (service layer) entre suas rotas/resolvers e a lógica de negócios para manter seu código organizado e modular, um padrão robusto para aplicações enterprise.

Parabéns por completar mais esta aula! Você deu um passo gigantesco em sua jornada de desenvolvimento de APIs, compreendendo e implementando os pilares da comunicação web moderna. Continue praticando e explorando!

🚀 Pronto para a próxima aula?

Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!

📚 Ver todas as aulas