Examples
Exemplos Node.js
Exemplos completos de integração com Node.js, Express, TypeScript e mais
Exemplos Node.js
Exemplos práticos e completos de como integrar o RenderHub em aplicações Node.js.
Instalação
npm install axios
# ou
yarn add axios
Configuração Básica
Variáveis de Ambiente
Crie um arquivo .env:
RENDERHUB_API_KEY=rh_live_sua_chave_aqui
RENDERHUB_BASE_URL=https://renderhub.com/api/v1
Cliente RenderHub
// lib/renderhub.js
const axios = require('axios');
require('dotenv').config();
class RenderHubClient {
constructor(apiKey = process.env.RENDERHUB_API_KEY) {
this.apiKey = apiKey;
this.baseURL = process.env.RENDERHUB_BASE_URL || 'https://renderhub.com/api/v1';
this.client = axios.create({
baseURL: this.baseURL,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
}
async render(options) {
try {
const response = await this.client.post('/render', options);
return response.data;
} catch (error) {
this.handleError(error);
}
}
async createTemplate(template) {
try {
const response = await this.client.post('/templates', template);
return response.data;
} catch (error) {
this.handleError(error);
}
}
async getTemplate(templateId) {
try {
const response = await this.client.get(`/templates/${templateId}`);
return response.data;
} catch (error) {
this.handleError(error);
}
}
handleError(error) {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
throw new Error(`Erro de autenticação: ${data.message}`);
case 403:
throw new Error(`Quota excedida: ${data.message}`);
case 429:
const retryAfter = data.retry_after || 60;
throw new Error(`Rate limit excedido. Tente novamente em ${retryAfter}s`);
default:
throw new Error(`Erro ${status}: ${data.message || error.message}`);
}
}
throw error;
}
}
module.exports = RenderHubClient;
Exemplo 1: Converter HTML para PDF
// examples/convert-html.js
const RenderHubClient = require('../lib/renderhub');
const fs = require('fs');
async function convertHTMLToPDF() {
const client = new RenderHubClient();
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
max-width: 800px;
margin: 0 auto;
}
h1 {
color: #2563eb;
border-bottom: 3px solid #2563eb;
padding-bottom: 10px;
}
.highlight {
background: #fef3c7;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>Relatório de Vendas - Janeiro 2024</h1>
<div class="highlight">
<h2>Resumo</h2>
<p>Total de vendas: R$ 150.000,00</p>
<p>Novos clientes: 42</p>
<p>Taxa de conversão: 15.8%</p>
</div>
<h2>Detalhes</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
</body>
</html>
`;
const result = await client.render({
mode: 'CONVERT',
input_type: 'html',
data: html,
page_size: 'A4',
orientation: 'portrait'
});
// Salvar PDF
const pdfBuffer = Buffer.from(result.pdf_base64, 'base64');
fs.writeFileSync('relatorio.pdf', pdfBuffer);
console.log('✅ PDF gerado com sucesso!');
console.log(`Páginas: ${result.pages}`);
console.log(`Tamanho: ${result.file_size_kb} KB`);
console.log(`Duração: ${result.duration_ms}ms`);
}
convertHTMLToPDF().catch(console.error);
Exemplo 2: Fatura com Template
// examples/generate-invoice.js
const RenderHubClient = require('../lib/renderhub');
const fs = require('fs');
// 1. Criar template (fazer apenas uma vez)
async function createInvoiceTemplate() {
const client = new RenderHubClient();
const template = await client.createTemplate({
name: 'invoice-template-v1',
description: 'Template de fatura padrão',
category: 'invoice',
content: fs.readFileSync('./templates/invoice.html', 'utf8'),
variables: [
{ name: 'company_name', type: 'string', required: true },
{ name: 'invoice_number', type: 'string', required: true },
{ name: 'customer_name', type: 'string', required: true },
{ name: 'items', type: 'array', required: true },
{ name: 'total', type: 'string', required: true }
]
});
console.log('Template criado:', template.id);
return template.id;
}
// 2. Gerar fatura a partir do template
async function generateInvoice(customerId, templateId) {
const client = new RenderHubClient();
// Buscar dados do cliente e pedidos (simulado)
const customer = {
id: customerId,
name: 'João Silva Comércio LTDA',
email: 'joao@example.com',
address: 'Rua das Flores, 123',
city: 'São Paulo',
state: 'SP'
};
const orders = [
{ product: 'Notebook Dell', quantity: 2, price: 3500.00 },
{ product: 'Mouse Logitech', quantity: 5, price: 150.00 },
{ product: 'Teclado Mecânico', quantity: 3, price: 450.00 }
];
// Mapear dados para o template
const invoiceData = {
company_name: 'Minha Empresa LTDA',
company_cnpj: '12.345.678/0001-99',
invoice_number: `2024-${customerId.toString().padStart(5, '0')}`,
invoice_date: new Date().toLocaleDateString('pt-BR'),
customer_name: customer.name,
customer_email: customer.email,
customer_address: `${customer.address}, ${customer.city} - ${customer.state}`,
items: orders.map(order => ({
description: order.product,
quantity: order.quantity,
unit_price: order.price.toFixed(2),
total: (order.quantity * order.price).toFixed(2)
})),
subtotal: orders.reduce((sum, o) => sum + (o.quantity * o.price), 0).toFixed(2),
tax: (orders.reduce((sum, o) => sum + (o.quantity * o.price), 0) * 0.15).toFixed(2),
total: (orders.reduce((sum, o) => sum + (o.quantity * o.price), 0) * 1.15).toFixed(2)
};
// Renderizar PDF
const result = await client.render({
mode: 'RENDER',
template_id: templateId,
data: invoiceData
});
// Salvar PDF
const pdfBuffer = Buffer.from(result.pdf_base64, 'base64');
const filename = `invoice-${invoiceData.invoice_number}.pdf`;
fs.writeFileSync(filename, pdfBuffer);
console.log(`✅ Fatura ${invoiceData.invoice_number} gerada!`);
return filename;
}
// Executar
(async () => {
const templateId = 'tpl_abc123'; // Ou criar com createInvoiceTemplate()
await generateInvoice(1001, templateId);
})();
Exemplo 3: Integração com Express
// server.js
const express = require('express');
const RenderHubClient = require('./lib/renderhub');
const app = express();
app.use(express.json());
const renderhub = new RenderHubClient();
// Endpoint para gerar PDF
app.post('/api/generate-pdf', async (req, res) => {
try {
const { template_id, data } = req.body;
// Validar dados
if (!template_id || !data) {
return res.status(400).json({
error: 'template_id e data são obrigatórios'
});
}
// Gerar PDF
const result = await renderhub.render({
mode: 'RENDER',
template_id,
data
});
// Retornar PDF em base64 ou enviar arquivo
res.json({
success: true,
pdf_base64: result.pdf_base64,
pages: result.pages,
file_size_kb: result.file_size_kb
});
} catch (error) {
console.error('Erro ao gerar PDF:', error);
res.status(500).json({
error: 'Erro ao gerar PDF',
message: error.message
});
}
});
// Endpoint para download direto
app.post('/api/download-pdf', async (req, res) => {
try {
const { template_id, data } = req.body;
const result = await renderhub.render({
mode: 'RENDER',
template_id,
data
});
// Converter base64 para buffer
const pdfBuffer = Buffer.from(result.pdf_base64, 'base64');
// Enviar como download
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename="document.pdf"');
res.send(pdfBuffer);
} catch (error) {
console.error('Erro:', error);
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});
Exemplo 4: Processamento em Lote com Fila
// examples/batch-processing.js
const Queue = require('bull');
const RenderHubClient = require('../lib/renderhub');
const fs = require('fs');
// Criar fila
const pdfQueue = new Queue('pdf-generation', {
redis: {
host: '127.0.0.1',
port: 6379
}
});
// Processar jobs
pdfQueue.process(async (job) => {
const { customerId, templateId } = job.data;
console.log(`Processando fatura para cliente ${customerId}...`);
const client = new RenderHubClient();
// Buscar dados do cliente
const customerData = await fetchCustomerData(customerId);
// Gerar PDF
const result = await client.render({
mode: 'RENDER',
template_id: templateId,
data: customerData
});
// Salvar
const pdfBuffer = Buffer.from(result.pdf_base64, 'base64');
const filename = `invoices/invoice-${customerId}.pdf`;
fs.writeFileSync(filename, pdfBuffer);
// Atualizar progresso
job.progress(100);
return { filename, customerId };
});
// Adicionar jobs à fila
async function generateInvoicesForAllCustomers(customerIds, templateId) {
for (const customerId of customerIds) {
await pdfQueue.add({
customerId,
templateId
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
});
}
console.log(`${customerIds.length} jobs adicionados à fila`);
}
// Eventos
pdfQueue.on('completed', (job, result) => {
console.log(`✅ Job ${job.id} concluído: ${result.filename}`);
});
pdfQueue.on('failed', (job, err) => {
console.error(`❌ Job ${job.id} falhou:`, err.message);
});
// Executar
const customerIds = [1001, 1002, 1003, 1004, 1005];
generateInvoicesForAllCustomers(customerIds, 'tpl_invoice_v1');
async function fetchCustomerData(customerId) {
// Simular busca no banco
return {
customer_name: `Cliente ${customerId}`,
invoice_number: `2024-${customerId}`,
total: '1500.00'
};
}
Exemplo 5: Webhooks para PDFs Assíncronos
// webhook-server.js
const express = require('express');
const RenderHubClient = require('./lib/renderhub');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const renderhub = new RenderHubClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
// Gerar PDF com webhook
app.post('/api/generate-async', async (req, res) => {
try {
const { template_id, data } = req.body;
const result = await renderhub.render({
mode: 'RENDER',
template_id,
data,
webhook_url: 'https://meuapp.com/webhooks/pdf-ready',
webhook_headers: {
'X-Custom-Token': 'meu-token-secreto'
}
});
// Retorna imediatamente com job_id
res.json({
success: true,
job_id: result.job_id,
status: result.status
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Receber callback do RenderHub
app.post('/webhooks/pdf-ready', async (req, res) => {
try {
// Validar assinatura HMAC
const signature = req.headers['x-renderhub-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Processar webhook
const { id, status, job_id, pdf_url, error } = req.body;
if (status === 'DONE') {
console.log(`✅ PDF ${id} pronto!`);
// Baixar PDF da URL temporária
const axios = require('axios');
const pdfResponse = await axios.get(pdf_url, {
responseType: 'arraybuffer'
});
// Salvar em S3, storage, etc
const fs = require('fs');
fs.writeFileSync(`pdfs/${id}.pdf`, pdfResponse.data);
// Atualizar status no banco de dados
await db.jobs.update(job_id, {
status: 'COMPLETED',
pdf_path: `pdfs/${id}.pdf`
});
// Notificar usuário (email, websocket, etc)
await notifyUser(job_id);
} else if (status === 'FAILED') {
console.error(`❌ Erro ao gerar PDF: ${error}`);
await db.jobs.update(job_id, {
status: 'FAILED',
error_message: error
});
}
// IMPORTANTE: Retornar 200 OK rapidamente
res.status(200).json({ received: true });
} catch (error) {
console.error('Erro ao processar webhook:', error);
res.status(500).json({ error: error.message });
}
});
app.listen(3000);
Exemplo 6: TypeScript
// lib/renderhub.ts
import axios, { AxiosInstance } from 'axios';
interface RenderOptions {
mode: 'CONVERT' | 'RENDER';
input_type?: 'html' | 'docx' | 'markdown';
data?: string | object;
template_id?: string;
page_size?: 'A4' | 'Letter' | 'Legal';
orientation?: 'portrait' | 'landscape';
}
interface RenderResult {
id: string;
status: 'DONE' | 'QUEUED' | 'PROCESSING' | 'FAILED';
pdf_base64?: string;
pages?: number;
file_size_kb?: number;
duration_ms?: number;
}
class RenderHubClient {
private client: AxiosInstance;
constructor(private apiKey: string) {
this.client = axios.create({
baseURL: 'https://renderhub.com/api/v1',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
}
async render(options: RenderOptions): Promise<RenderResult> {
try {
const response = await this.client.post<RenderResult>('/render', options);
return response.data;
} catch (error: any) {
if (error.response) {
throw new Error(`RenderHub API Error: ${error.response.data.message}`);
}
throw error;
}
}
async savePDF(result: RenderResult, filename: string): Promise<void> {
if (!result.pdf_base64) {
throw new Error('PDF base64 não disponível');
}
const fs = await import('fs');
const pdfBuffer = Buffer.from(result.pdf_base64, 'base64');
fs.writeFileSync(filename, pdfBuffer);
}
}
export default RenderHubClient;
Exemplo 7: Retry com Backoff Exponencial
// lib/retry.js
async function generatePDFWithRetry(renderFn, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await renderFn();
} catch (error) {
const isLastAttempt = attempt === maxRetries - 1;
// Se rate limited, aguardar
if (error.message.includes('Rate limit')) {
if (isLastAttempt) throw error;
const delay = Math.min(2 ** attempt * 1000, 30000); // Max 30s
console.log(`Rate limited. Aguardando ${delay/1000}s...`);
await sleep(delay);
continue;
}
// Outros erros, não fazer retry
throw error;
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Uso
const client = new RenderHubClient();
const result = await generatePDFWithRetry(async () => {
return await client.render({
mode: 'CONVERT',
input_type: 'html',
data: '<h1>Test</h1>'
});
});
Próximos Passos
- Exemplos Python - Integração com Python/Flask/Django
- Exemplos PHP - Integração com PHP/Laravel
- API Reference - Documentação completa da API
- Guia de Templates - Templates e variáveis