swiftly

0.0.4-b3 • Public • Published

Swiftly HTTP Client

Um cliente HTTP Node.js poderoso e flexível com recursos avançados de scraping, circuit breaking, caching e muito mais.

MIT License Node Version NPM Version

📚 Índice

⚡ Recursos

Zero dependências não-nativas

  • Baseado apenas em módulos nativos do Node.js
  • Código limpo e eficiente
  • Fácil manutenção e atualização

🔄 Retry automático com backoff exponencial

  • Tentativas configuráveis
  • Delay exponencial entre tentativas
  • Tratamento inteligente de falhas

🛡️ Circuit breaker por domínio

  • Proteção contra falhas em cascata
  • Estados: CLOSED, OPEN, HALF-OPEN
  • Configuração por domínio

🚀 HTTP/2 e IPv6

  • Suporte completo a HTTP/2
  • Multiplexação de requests
  • Compatibilidade com IPv6

📊 Métricas detalhadas

  • Tempo de resposta
  • Taxa de sucesso/erro
  • Uso de cache
  • Métricas por rota

🔍 Scraping integrado

  • Parser HTML inteligente
  • Seletores CSS
  • Transformação de dados

🎯 GraphQL e SSE

  • Queries e mutations
  • Subscrições em tempo real
  • Validação de schema

🍪 Gerenciamento de sessões

  • Cookies persistentes
  • TTL configurável
  • Limpeza automática

Cache inteligente

  • Cache em memória
  • TTL configurável
  • Invalidação automática

🔒 Headers anti-bot

  • User-Agent rotation
  • Headers customizáveis
  • Fingerprint aleatório

📝 Logs granulares

  • Níveis de log configuráveis
  • Formato customizável
  • Debug mode

🔄 Interceptadores

  • Pre/post request
  • Transformação de resposta
  • Validação de dados

⏱️ Timeouts granulares

  • Connect timeout
  • Response timeout
  • Idle timeout

💾 Compressão automática

  • GZIP
  • Deflate
  • Brotli

🚀 Instalação

npm install swiftly

📘 Uso Básico

GET Request Simples

const swiftly = require('swiftly');

// GET request básico
const response = await swiftly.get('https://api.exemplo.com/posts');
console.log(response.data);

// Com query params
const userPosts = await swiftly.get('https://api.exemplo.com/posts', {
  params: {
    userId: 1,
    status: 'published'
  }
});

POST com JSON

const post = await swiftly.post('https://api.exemplo.com/posts', {
  title: 'Novo Post',
  body: 'Conteúdo do post',
  tags: ['javascript', 'node']
});

Upload de Arquivo

const fileBuffer = Buffer.from('Conteúdo do arquivo');
fileBuffer.name = 'documento.txt';
fileBuffer.type = 'text/plain';

const response = await swiftly.post('https://api.exemplo.com/upload', {
  file: fileBuffer,
  description: 'Meu documento'
}, {
  formData: true
});

Scraping Básico

// Scraping simples
const titles = await swiftly.scrape('https://exemplo.com', '.post-title');

// Scraping com múltiplos seletores
const data = await swiftly.scrape('https://exemplo.com', {
  title: 'h1.main-title',
  description: 'meta[name="description"]',
  posts: {
    selector: '.post',
    transform: (el) => ({
      title: el.querySelector('h2').textContent,
      link: el.querySelector('a').href,
      date: new Date(el.querySelector('.date').textContent)
    })
  }
});

GraphQL Query

const data = await swiftly.query(`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
      email
      posts {
        id
        title
      }
    }
  }
`, {
  id: 1
});

Server-Sent Events

const subscription = swiftly.subscribe('https://api.exemplo.com/events', {
  onMessage: (data) => console.log('Novo evento:', data),
  onError: (error) => console.error('Erro:', error),
  onOpen: () => console.log('Conexão estabelecida'),
  reconnect: true,
  maxRetries: 5
});

⚙️ Configuração Avançada

const client = swiftly({
  // Timeouts (ms)
  timeouts: {
    connect: 5000,    // Timeout de conexão
    response: 30000,  // Timeout de resposta
    idle: 60000      // Timeout de inatividade
  },

  // Cache
  cache: {
    enabled: true,
    ttl: 300000,     // 5 minutos
    maxSize: 1000    // Máximo de itens
  },

  // Rate Limiting
  rateLimiting: {
    enabled: true,
    requestsPerSecond: 2,
    maxDelay: 64000, // Máximo delay entre requests
    minDelay: 1000   // Mínimo delay entre requests
  },

  // Circuit Breaker
  circuitBreaker: {
    enabled: true,
    failureThreshold: 5,
    resetTimeout: 60000
  },

  // Sessões
  session: {
    ttl: 3600000,    // 1 hora
    maxSessions: 100,
    autoCleanup: true
  },

  // Compressão
  compression: {
    request: true,   // Comprimir requests
    response: true,  // Descomprimir responses
    minSize: 1024    // Tamanho mínimo para comprimir
  },

  // Debug
  debug: true,       // Habilita logs detalhados

  // HTTP/2
  useHttp2: true,    // Habilita suporte a HTTP/2

  // Validação SSL
  validateSSL: true, // Validar certificados SSL

  // Redirects
  followRedirects: true,
  maxRedirects: 5
});

🔍 HTTP Methods

GET

// GET básico
const response = await client.get('https://api.exemplo.com/data');

// Com query params
const filtered = await client.get('https://api.exemplo.com/posts', {
  params: {
    category: 'tech',
    page: 1
  }
});

// Com headers customizados
const protected = await client.get('https://api.exemplo.com/protected', {
  headers: {
    'Authorization': 'Bearer token'
  }
});

POST

// POST com JSON
const created = await client.post('https://api.exemplo.com/posts', {
  title: 'Título',
  content: 'Conteúdo'
});

// POST com form data
const formData = await client.post('https://api.exemplo.com/form', {
  name: 'João',
  age: 25
}, {
  formData: true
});

// POST com arquivo
const fileUpload = await client.post('https://api.exemplo.com/upload', {
  file: fileBuffer,
  type: 'document'
}, {
  formData: true
});

PUT

const updated = await client.put('https://api.exemplo.com/posts/1', {
  title: 'Título Atualizado'
});

DELETE

const deleted = await client.delete('https://api.exemplo.com/posts/1');

📝 Sistema de Logs

const client = swiftly({ debug: true });

// Os logs incluem:
// - Requests e responses
// - Cache hits/misses
// - Circuit breaker state
// - Rate limiting delays
// - Session cleanup
// - Erros e retries

// Exemplo de saída:
// [Swiftly 2024-03-19T10:15:30.123Z] GET request to https://api.exemplo.com
// [Swiftly 2024-03-19T10:15:30.456Z] Response: 200 OK

🔄 Interceptadores

const client = swiftly();

// Request interceptor
client.interceptors.request.use((config) => {
  // Adicionar headers
  config.headers['Authorization'] = 'Bearer token';
  return config;
});

// Response interceptor com validação
client.interceptors.response.use((response) => {
  const schema = {
    id: { type: 'number', required: true },
    title: { type: 'string', minLength: 1 }
  };
  
  validateSchema(response.data, schema);
  return response;
});

🛡️ Circuit Breaker

const client = swiftly({
  circuitBreaker: {
    enabled: true,
    failureThreshold: 5,
    resetTimeout: 60000
  }
});

// Monitorar eventos do circuit breaker
client.on('circuit:open', (data) => {
  console.log(`Circuit breaker aberto para: ${data.domain}`);
  console.log(`Falhas: ${data.failureCount}`);
  console.log(`Reset em: ${data.resetTimeout}ms`);
});

client.on('circuit:close', (data) => {
  console.log(`Circuit breaker fechado para: ${data.domain}`);
});

client.on('circuit:half-open', (data) => {
  console.log(`Circuit breaker em half-open para: ${data.domain}`);
});

💾 Cache e Rate Limiting

const client = swiftly({
  cache: {
    enabled: true,
    ttl: 300000
  },
  rateLimiting: {
    enabled: true,
    requestsPerSecond: 2
  }
});

// Eventos de cache
client.on('cache:hit', (data) => console.log('Cache hit:', data.url));
client.on('cache:miss', (data) => console.log('Cache miss:', data.url));

// Eventos de rate limiting
client.on('rate:limit', (data) => {
  console.log(`Rate limit atingido: ${data.delay}ms delay`);
});

// Rate limiting adaptativo
client.on('rate:adapt', (data) => {
  console.log(`Novo rate limit: ${data.requestsPerSecond}/s`);
});

🍪 Sessões e Cookies

const client = swiftly({
  session: {
    ttl: 3600000,
    maxSessions: 100
  }
});

// Login e persistência de sessão
const auth = await client.post('https://api.exemplo.com/login', {
  username: 'user',
  password: 'pass'
});

// Requests autenticados usam a sessão automaticamente
const profile = await client.get('https://api.exemplo.com/profile');

// Eventos de sessão
client.on('sessions:cleanup', (data) => {
  console.log(`${data.cleaned} sessões limpas`);
});

🕷️ Scraping

// Scraping com transformação
const articles = await swiftly.scrape('https://blog.exemplo.com', {
  selector: 'article',
  transform: (el) => ({
    title: el.querySelector('h2').textContent,
    link: el.querySelector('a').href,
    date: new Date(el.querySelector('.date').textContent),
    tags: Array.from(el.querySelectorAll('.tag')).map(tag => tag.textContent),
    author: {
      name: el.querySelector('.author-name').textContent,
      avatar: el.querySelector('.author-avatar').src
    }
  })
});

// Scraping paginado
async function* scrapeAllPages(url) {
  let currentPage = 1;
  while (true) {
    const articles = await swiftly.scrape(`${url}?page=${currentPage}`, '.article');
    if (articles.length === 0) break;
    yield articles;
    currentPage++;
  }
}

📊 Métricas

console.log(client.metrics);
/*
{
  requestCount: 150,
  successCount: 145,
  errorCount: 5,
  totalTime: 2500,
  averageResponseTime: 16.67,
  cacheHits: 50,
  cacheMisses: 100,
  retries: 3,
  totalDataTransferred: 1048576,
  http2Requests: 75
}
*/

// Métricas por rota
console.log(client.routeMetrics);
/*
{
  'GET /api/users': {
    count: 50,
    totalTime: 750,
    avgTime: 15
  }
}
*/

❌ Tratamento de Erros

try {
  await client.get('https://api.exemplo.com/data');
} catch (error) {
  if (error.name === 'ValidationError') {
    console.log('Erro de validação:', error.message);
    console.log('Contexto:', error.context);
  } 
  else if (error.name === 'CircuitBreakerError') {
    console.log('Circuit breaker aberto para:', error.domain);
    console.log('Reset em:', error.resetTimeout);
  } 
  else if (error.name === 'TimeoutError') {
    console.log('Tipo de timeout:', error.type); // connect, response, idle
    console.log('Limite:', error.timeout);
  } 
  else if (error.name === 'RateLimitError') {
    console.log('Rate limit excedido');
    console.log('Próxima tentativa em:', error.nextAttempt);
  }
  else if (error.response) {
    // Erro HTTP com resposta
    console.log('Status:', error.response.status);
    console.log('Data:', error.response.data);
  }
}

🖥️ CLI

# GET request
swiftly get https://api.exemplo.com/data

# GET com query params
swiftly get https://api.exemplo.com/posts --params '{"userId":1}'

# POST com dados
swiftly post https://api.exemplo.com/posts -d '{"title":"Novo Post"}'

# Scraping
swiftly scrape https://exemplo.com -s '.post-title'

# Scraping com transformação
swiftly scrape https://exemplo.com -s '.post' --transform './transform.js'

# GraphQL query
swiftly query 'query { users { id name } }'

# Monitorar eventos
swiftly events https://api.exemplo.com/stream

# Upload de arquivo
swiftly upload https://api.exemplo.com/upload ./arquivo.txt

# Debug mode
swiftly get https://api.exemplo.com/data --debug

📚 Exemplos Práticos

Autenticação e Sessões

const client = swiftly();

// Login
const auth = await client.post('https://api.exemplo.com/login', {
  username: 'user',
  password: 'pass'
});

// Requests autenticados usam a sessão automaticamente
const profile = await client.get('https://api.exemplo.com/profile');

Upload de Arquivos

const file = Buffer.from('Conteúdo do arquivo');
file.name = 'documento.txt';
file.type = 'text/plain';

const response = await client.post('https://api.exemplo.com/upload', {
  file: file,
  description: 'Meu documento'
}, {
  formData: true
});

Rate Limiting Adaptativo

const client = swiftly({
  rateLimiting: {
    enabled: true,
    requestsPerSecond: 2,
    adaptive: true,
    maxDelay: 64000,
    minDelay: 1000
  }
});

// O rate limiting se adapta baseado nas respostas do servidor
client.on('rate:adapt', (data) => {
  console.log(`Novo rate limit: ${data.requestsPerSecond}/s`);
});

Scraping com Paginação

async function scrapeAllArticles() {
  const articles = [];
  let page = 1;
  
  while (true) {
    const pageArticles = await swiftly.scrape(`https://blog.exemplo.com/page/${page}`, {
      selector: 'article',
      transform: (el) => ({
        title: el.querySelector('h2').textContent,
        link: el.querySelector('a').href,
        date: new Date(el.querySelector('.date').textContent)
      })
    });
    
    if (pageArticles.length === 0) break;
    articles.push(...pageArticles);
    page++;
  }
  
  return articles;
}

GraphQL com Subscrições

// Query
const data = await client.query(`
  query GetUserPosts($userId: ID!) {
    user(id: $userId) {
      posts {
        id
        title
      }
    }
  }
`, {
  userId: 1
});

// Mutation
const newPost = await client.query(`
  mutation CreatePost($input: PostInput!) {
    createPost(input: $input) {
      id
      title
    }
  }
`, {
  input: {
    title: 'Novo Post',
    body: 'Conteúdo'
  }
});

// Subscription
const unsubscribe = await client.subscribe(`
  subscription OnNewPost {
    newPost {
      id
      title
    }
  }
`, {
  onData: (post) => console.log('Novo post:', post),
  onError: (error) => console.error('Erro:', error)
});

Monitoramento Completo

const client = swiftly({ debug: true });

// Monitorar todos os eventos importantes
[
  'request:start',
  'request:end',
  'cache:hit',
  'cache:miss',
  'circuit:open',
  'circuit:close',
  'sessions:cleanup',
  'rate:limit',
  'progress'
].forEach(event => {
  client.on(event, (data) => {
    console.log(`[${event}]`, data);
    
    // Salvar métricas
    if (event === 'request:end') {
      saveMetrics({
        url: data.url,
        method: data.method,
        status: data.status,
        time: data.time
      });
    }
  });
});

❓ FAQ

Como lidar com timeouts?

const client = swiftly({
  timeouts: {
    connect: 5000,    // Timeout de conexão
    response: 30000,  // Timeout de resposta
    idle: 60000      // Timeout de inatividade
  }
});

try {
  await client.get('https://api.exemplo.com/slow-endpoint');
} catch (error) {
  if (error.name === 'TimeoutError') {
    console.log(`Timeout do tipo ${error.type}`);
  }
}

Como validar respostas?

client.interceptors.response.use((response) => {
  const schema = {
    id: { type: 'number', required: true },
    title: { type: 'string', minLength: 1 },
    date: { type: 'string', format: 'date-time' }
  };
  
  validateSchema(response.data, schema);
  return response;
});

Como lidar com retry?

const client = swiftly({
  retries: 3,
  retryDelay: 1000,
  retryCondition: (error) => {
    return error.status === 429 || error.status >= 500;
  }
});

🤝 Contribuição

Contribuições são sempre bem-vindas! Por favor, leia nosso guia de contribuição antes de submeter um pull request.

Como contribuir:

  1. Fork o projeto
  2. Crie sua feature branch (git checkout -b feature/AmazingFeature)
  3. Commit suas mudanças (git commit -m 'Add some AmazingFeature')
  4. Push para a branch (git push origin feature/AmazingFeature)
  5. Abra um Pull Request

📄 Licença

MIT

👨‍💻 Autor

Cognima

🙏 Agradecimentos

  • Node.js core team
  • Todos os contribuidores
  • Nossa comunidade incrível

⭐ Se este projeto te ajudou, por favor considere dar uma estrela no GitHub!

Package Sidebar

Install

npm i swiftly

Weekly Downloads

65

Version

0.0.4-b3

License

MIT

Unpacked Size

80.2 kB

Total Files

13

Last publish

Collaborators

  • cognima