Custom nodes permitem executar lógica personalizada nos seus próprios servidores, integrando o Triglit completamente com sua infraestrutura.
O que são Custom Nodes?
Custom nodes são nodes que executam em seus próprios servidores via webhooks. Quando um custom node é executado:
- O Triglit faz uma requisição HTTP para seu endpoint
- Seu servidor processa a requisição
- Você retorna o resultado
- O Triglit continua a execução do workflow
Configuração
1. Registrar Custom Node
No dashboard do Triglit, registre seu custom node:
{
"id": "custom-payment-processor",
"name": "Processar Pagamento",
"endpoint": "https://api.seudominio.com/triglit/custom-nodes",
"description": "Processa pagamentos via gateway"
}
2. Configurar Endpoint
Seu endpoint deve aceitar requisições POST:
// Express.js example
app.post('/triglit/custom-nodes', async (req, res) => {
const { nodeId, context, config } = req.body;
// Processar lógica customizada
const result = await processPayment(context.data.input);
// Retornar resultado
res.json({
success: true,
data: result
});
});
Estrutura da Requisição
Quando o Triglit chama seu custom node:
{
"nodeId": "custom-payment-processor",
"context": {
"data": {
"input": { "orderId": "order_123", "amount": 100.00 },
"node-1": { "result": "validated" }
},
"metadata": {
"runId": "run_456",
"entityId": "order_123",
"tenantId": "tenant_abc"
}
},
"config": {
"action": "process-payment",
"gateway": "stripe"
}
}
Estrutura da Resposta
Seu endpoint deve retornar:
{
"success": true,
"data": {
"paymentId": "pay_123",
"status": "succeeded",
"amount": 100.00
}
}
Resposta de Erro
{
"success": false,
"error": {
"message": "Payment failed",
"code": "PAYMENT_DECLINED"
}
}
Autenticação
O Triglit envia autenticação no header:
X-Triglit-Signature: signature_hash
X-Triglit-Timestamp: 1640995200
Valide a assinatura:
const crypto = require('crypto');
function validateSignature(body, signature, timestamp, secret) {
const payload = `${timestamp}.${JSON.stringify(body)}`;
const hash = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return hash === signature;
}
app.post('/triglit/custom-nodes', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-triglit-signature'];
const timestamp = req.headers['x-triglit-timestamp'];
if (!validateSignature(req.body, signature, timestamp, process.env.TRIGLIT_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Processar requisição
});
Exemplo Completo
Node de Processamento de Pagamento
// Node: Processar Pagamento
app.post('/triglit/custom-nodes', async (req, res) => {
try {
const { nodeId, context, config } = req.body;
// Validar autenticação
if (!validateSignature(req.body, req.headers['x-triglit-signature'],
req.headers['x-triglit-timestamp'],
process.env.TRIGLIT_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Extrair dados do contexto
const orderData = context.data.input;
// Processar pagamento
const payment = await stripe.charges.create({
amount: orderData.amount * 100, // em centavos
currency: 'usd',
source: orderData.paymentToken,
description: `Order ${orderData.orderId}`
});
// Retornar resultado
res.json({
success: true,
data: {
paymentId: payment.id,
status: payment.status,
amount: payment.amount / 100
}
});
} catch (error) {
res.status(500).json({
success: false,
error: {
message: error.message,
code: 'PAYMENT_ERROR'
}
});
}
});
Usar no Workflow
{
"nodes": [
{
"id": "process-payment",
"type": "custom",
"config": {
"nodeId": "custom-payment-processor",
"endpoint": "https://api.seudominio.com/triglit/custom-nodes",
"method": "POST",
"params": {
"action": "process-payment",
"gateway": "stripe"
}
}
}
]
}
Timeout e Retries
Timeout
Configure timeout no node:
{
"type": "custom",
"config": {
"timeout": 30000 // 30 segundos
}
}
Retries
O Triglit retenta automaticamente em caso de falha:
{
"type": "custom",
"config": {
"retry": {
"maxAttempts": 3,
"backoff": "exponential",
"initialDelay": 1000
}
}
}
Idempotência
Torne seus custom nodes idempotentes:
app.post('/triglit/custom-nodes', async (req, res) => {
const { context, config } = req.body;
const orderId = context.data.input.orderId;
// Verificar se já foi processado
const existing = await db.payments.findOne({ orderId });
if (existing) {
return res.json({
success: true,
data: existing,
idempotent: true
});
}
// Processar pagamento
const payment = await processPayment(orderId);
// Salvar resultado
await db.payments.create(payment);
res.json({
success: true,
data: payment
});
});
Boas Práticas
Sempre valide dados de entrada antes de processar.
Torne nodes idempotentes para permitir retries seguros.
Configure timeouts realistas baseados na complexidade da operação.
Sempre retorne erros estruturados e informativos.
Logue todas as execuções para debugging e auditoria.
Valide assinaturas e use HTTPS sempre.
Limitações
- Timeout máximo: 5 minutos por node
- Tamanho de payload: 1MB
- Retries: Máximo de 3 tentativas (configurável)
Para operações longas, considere retornar imediatamente e processar de forma assíncrona, notificando o Triglit via webhook quando concluído.