Seu carrinho está vazio no momento!

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 (
GETpara obter,POSTpara criar,PUTpara atualizar completamente,PATCHpara atualizar parcialmente eDELETEpara 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
DELETEouPUT), 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 seupackage.jsontenha um scriptstartapontando para seu arquivo principal (ex:"start": "node index.js"). - A variável de ambiente
PORTé tipicamente injetada pelo ambiente do host. Nosso códigoprocess.env.PORT || 3000garante que ele use a porta fornecida pelo HostGator ou a padrão3000se 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 seupackage.jsonpara 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 oucurlcom 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/graphqlno 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-fahrenheitA 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/productspara listar todos os produtos. - Implemente uma rota
GET /api/products/:idpara obter um produto específico. - Implemente uma rota
POST /api/productspara adicionar um novo produto. Inclua validação básica paranameeprice.
- Crie uma nova base de dados em memória para
- Para a API GraphQL:
- Adicione um novo tipo
Productao seutypeDefs(com os camposid,name,price). - Adicione uma nova query
allProductsque retorne uma lista de[Product]. - Adicione uma nova query
product(id: ID!): Productpara buscar um produto pelo ID. - Adicione uma nova mutation
createProduct(name: String!, price: Float!): Productpara criar um produto. - Implemente os resolvers correspondentes para essas novas queries e mutation.
- Adicione um novo tipo
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
Producte as queries/mutations GraphQL:Modifique
typeDefspara incluir o novo tipo e operações:const typeDefs = gqltype 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
resolverspara 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/productsVerifique 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.
- Acesse
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 seuindex.js. - Erros de JSON ou corpo vazio em POST: Certifique-se de que está enviando
Content-Type: application/jsonno cabeçalho e que o corpo da sua requisiçãoPOSTé 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
typeDefseresolverscorrespondem. - 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
PUTeDELETEpara os recursosuserseproducts. Adicione autenticação e autorização às suas rotas. - Para GraphQL: Aprofunde-se nos conceitos de
DataLoaderpara resolver o problema N+1, otimizando o carregamento de dados. Explore asSubscriptionspara 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!