Integração de Sistemas: APIs REST, Webhooks e GraphQL em Ação

Integração de Sistemas: APIs REST, Webhooks e GraphQL em Ação
Objetivo – Mostrar, com código funcional, como projetar e implementar integrações entre sistemas usando três abordagens complementares: APIs REST, Webhooks e GraphQL. O artigo inclui boas práticas de versionamento, segurança, tratamento de erros e orquestração via API Gateway.
📌 Introdução
Em ambientes corporativos modernos, a troca de informações entre aplicações raramente acontece de forma isolada. Seja um ERP que precisa notificar um CRM, um serviço de pagamento que avisa um marketplace ou um front‑end que consome dados de múltiplas fontes, a integração via API tornou‑se a espinha dorsal da arquitetura de negócios.
Existem três padrões que, combinados, cobrem a maioria dos casos de uso:
| Padrão | Quando usar | Principais vantagens |
|---|---|---|
| REST | Operações CRUD, recursos bem definidos, alta interoperabilidade | Simplicidade, cache HTTP, amplo suporte |
| Webhooks | Notificações assíncronas, eventos em tempo real, redução de polling | Latência baixa, menor carga no provedor |
| GraphQL | Consultas flexíveis, múltiplas fontes de dados, front‑ends ricos | Evita over/under‑fetch, versionamento implícito |
Neste post vamos criar exemplos reais que podem ser copiados para projetos de qualquer porte. O foco será em Node.js (para REST), Python (para Webhooks) e Apollo Server (para GraphQL), mas os princípios são transferíveis para outras linguagens.
1️⃣ Construindo uma API REST robusta
1.1. Principais decisões de design
/api/v1/customers1.2. Implementação em Node.js (Express)
// src/server.js
const express = require('express');
const helmet = require('helmet');
const morgan = require('morgan');
const { expressjwt: jwt } = require('express-jwt');
const jwksRsa = require('jwks-rsa');
const app = express();
// Middlewares essenciais
app.use(helmet());
app.use(express.json());
app.use(morgan('combined'));
// Configuração OAuth2 (Auth0 como exemplo)
const checkJwt = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksUri: 'https://YOUR_DOMAIN/.well-known/jwks.json'
}),
audience: 'https://api.seusistema.com',
issuer: 'https://YOUR_DOMAIN/',
algorithms: ['RS256']
});
// Rotas versionadas
const router = express.Router();
router.get('/customers', async (req, res, next) => {
try {
// Simulação de busca no banco
const customers = await db.query('SELECT FROM customers LIMIT 100');
res.json(customers);
} catch (err) {
next(err);
}
});
router.post('/customers', async (req, res, next) => {
try {
const { name, email } = req.body;
const result = await db.query(
'INSERT INTO customers (name, email) VALUES ($1, $2) RETURNING ',
[name, email]
);
res.status(201).json(result.rows[0]);
} catch (err) {
next(err);
}
});
// Aplicando autenticação apenas nas rotas que exigem
app.use('/api/v1', checkJwt, router);
// Middleware de erro padronizado
app.use((err, _req, res, _next) => {
const status = err.status || 500;
res.status(status).json({
type: 'https://example.com/problem',
title: err.message,
status,
detail: err.stack
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(API rodando na porta ${PORT}));
Destaques do código
helmet protege contra vulnerabilidades de cabeçalhos HTTP.
express-jwt + jwks-rsa valida tokens assinados por um provedor OAuth2, evitando a necessidade de armazenar chaves secretas.
Middleware de erro devolve um objeto conforme RFC 7807, facilitando o consumo por clientes automatizados.
1.3. Testando a API com curl
# Listar clientes (token fictício)
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
https://api.seusistema.com/api/v1/customers
Criar cliente
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"name":"Ana Silva","email":"ana@example.com"}' \
https://api.seusistema.com/api/v1/customers
2️⃣ Webhooks: comunicação orientada a eventos
2.1. Por que usar Webhooks?
Push imediato – O provedor envia o evento assim que ele ocorre. Redução de chamadas – Elimina o polling constante, economizando banda e recursos. Desacoplamento – O receptor pode processar o evento de forma assíncrona.
2.2. Segurança dos Webhooks
| Estratégia | Implementação |
|---|---|
| Assinatura HMAC | O provedor inclui um cabeçalho X-Signature calculado com um segredo compartilhado. |
| IP Whitelisting | Permitir apenas requisições de faixas IP conhecidas. |
| TLS | Exigir HTTPS para evitar intercepção. |
2.3. Receptor em Python (Flask)
# webhook_receiver.py
import hmac
import hashlib
import json
from flask import Flask, request, abort
app = Flask(__name__)
Segredo compartilhado entre os sistemas
WEBHOOK_SECRET = b'seu_segredo_super_secreto'
def verify_signature(payload, signature):
expected = hmac.new(WEBHOOK_SECRET, payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhook/orders', methods=['POST'])
def orders_webhook():
signature = request.headers.get('X-Signature')
if not signature or not verify_signature(request.data, signature):
abort(401, description='Assinatura inválida')
event = request.get_json()
# Processamento assíncrono (ex.: enfileirar no RabbitMQ)
print(f"Evento recebido: {event['type']} - ID {event['data']['order_id']}")
return '', 204
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
2.4. Emissor de Webhook (Node.js)
// sender.js
const axios = require('axios');
const crypto = require('crypto');
const WEBHOOK_URL = 'https://meuservico.com/webhook/orders';
const SECRET = 'seu_segredo_super_secreto';
function signPayload(payload) {
return crypto
.createHmac('sha256', SECRET)
.update(JSON.stringify(payload))
.digest('hex');
}
async function sendOrderCreated(order) {
const payload = {
type: 'order.created',
data: order
};
const signature = signPayload(payload);
await axios.post(WEBHOOK_URL, payload, {
headers: {
'Content-Type': 'application/json',
'X-Signature': signature
},
timeout: 5000
});
}
// Exemplo de uso
sendOrderCreated({ order_id: 12345, amount: 250.0 })
.then(() => console.log('Webhook enviado'))
.catch(err => console.error('Falha ao enviar webhook:', err));
Dica: Use filas (RabbitMQ, SQS) no receptor para garantir resiliência caso o processamento demore ou falhe.
3️⃣ GraphQL: consultas flexíveis em um único endpoint
3.1. Quando GraphQL faz sentido?
Front‑ends móveis que precisam de apenas alguns campos de um recurso grande. Dashboard unificado que combina dados de múltiplas APIs.
- Evolução de schema sem versionamento explícito.
3.2. Schema básico com Apollo Server
``js
// graphql-server.js
const { ApolloServer, gql } = require('apollo-server');
const fetch = require('node-fetch');
// Definição do schema const typeDefs = gql type Customer { id: ID! name: String! email: String! orders: [Order!]! }
type Order { id: ID! total: Float! createdAt: String! }
type Query {
customers: [Customer!]!
customer(id: ID!): Customer
}
;
// Resolvers que consomem duas APIs distintas (REST + outro serviço) const resolvers = { Query: { customers: async () => { const resp = await fetch('https://api.seusistema.com/api/v1/customers'); return resp.json(); }, customer: async (_, { id }) => { const resp = await fetch(https://api.seusistema.com/api/v1/custom


