Pular para conteúdo

Expressões e Fórmulas

Expressões permitem criar lógica complexa e transformações de dados avançadas no Flow Builder. Esta página cobre expressões, fórmulas e técnicas avançadas.

Sintaxe de Expressões

Expressões Básicas

Expressões são envolvidas por {{ }} e podem conter JavaScript válido:

// Simples
{{ 2 + 2 }}

// Com variáveis
{{ $vars.price * 1.1 }}

// Booleanas
{{ $vars.age >= 18 }}

// Strings
{{ `Olá, ${$vars.name}!` }}

Expressões em Configurações

{
  "url": "{{ $vars.apiUrl }}/users/{{ $trigger.body.userId }}",
  "headers": {
    "Authorization": "Bearer {{ $vars.token }}",
    "X-Request-ID": "{{ $context.executionId }}"
  },
  "body": {
    "processed": "{{ new Date().toISOString() }}",
    "data": "{{ JSON.stringify($node.output) }}"
  }
}

Operadores Avançados

Operador Ternário

// Sintaxe: condição ? valorVerdadeiro : valorFalso
{{ $vars.isPremium ? 'premium' : 'free' }}

// Aninhado
{{ $vars.score > 90 ? 'A' : $vars.score > 80 ? 'B' : 'C' }}

// Com expressões complexas
{{ $vars.isActive && $vars.verified ? $vars.fullAccess : $vars.limitedAccess }}

Operador Nullish Coalescing (??)

// Retorna o segundo valor apenas se o primeiro for null ou undefined
{{ $vars.customUrl ?? 'https://default.com' }}

// Diferente de || que também considera '', 0, false
{{ $vars.count ?? 10 }} // Se count for 0, retorna 0 (não 10)
{{ $vars.count || 10 }} // Se count for 0, retorna 10

Optional Chaining (?.)

// Evita erros se a propriedade não existir
{{ $node.output.data?.user?.email }}

// Com arrays
{{ $node.output.items?.[0]?.name }}

// Com funções
{{ $vars.customHandler?.() }}

Spread Operator

// Arrays
{{ [...$vars.defaultItems, ...$node.output.newItems] }}

// Objects
{{ { ...$vars.defaults, ...$trigger.body } }}

Funções Personalizadas

Arrow Functions

// Map com arrow function
{{ $node.output.users.map(u => u.email) }}

// Filter com condição
{{ $node.output.products.filter(p => p.price > 100 && p.inStock) }}

// Reduce para soma
{{ $node.output.items.reduce((sum, item) => sum + item.price, 0) }}

Funções Complexas

// Processamento multi-step
{{
  $node.output.orders
    .filter(o => o.status === 'pending')
    .map(o => ({
      id: o.id,
      total: o.items.reduce((sum, i) => sum + i.price * i.qty, 0),
      customer: o.customer.email
    }))
    .sort((a, b) => b.total - a.total)
}}

Expressões Condicionais

If/Else em Expressões

// Validação de email
{{
  $trigger.body.email &&
  $trigger.body.email.includes('@') &&
  $trigger.body.email.length > 5
}}

// Status baseado em score
{{
  $vars.score >= 90 ? 'excellent' :
  $vars.score >= 70 ? 'good' :
  $vars.score >= 50 ? 'average' : 'poor'
}}

Switch Case Simulado

// Usando objeto como switch
{{
  {
    'pending': 'Aguardando',
    'processing': 'Processando',
    'completed': 'Concluído',
    'failed': 'Falhou'
  }[$vars.status] || 'Desconhecido'
}}

Transformações de Dados

Normalização de Dados

// Normalizar telefone
{{ $trigger.body.phone.replace(/\D/g, '') }}

// Normalizar email
{{ $trigger.body.email.toLowerCase().trim() }}

// Normalizar nome
{{
  $trigger.body.name
    .trim()
    .split(' ')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join(' ')
}}

Agregação de Dados

// Somar valores
{{ $node.output.items.reduce((sum, item) => sum + item.value, 0) }}

// Média
{{
  $node.output.scores.reduce((sum, s) => sum + s, 0) /
  $node.output.scores.length
}}

// Agrupar por categoria
{{
  $node.output.products.reduce((acc, p) => {
    acc[p.category] = acc[p.category] || [];
    acc[p.category].push(p);
    return acc;
  }, {})
}}

Filtragem e Ordenação

// Filtrar e ordenar
{{
  $node.output.users
    .filter(u => u.active && u.verified)
    .sort((a, b) => b.score - a.score)
    .slice(0, 10)
}}

// Remover duplicatas
{{ [...new Set($node.output.emails)] }}

// Filtrar valores únicos por propriedade
{{
  $node.output.items
    .filter((item, index, self) =>
      self.findIndex(i => i.id === item.id) === index
    )
}}

Manipulação de Strings

Templates Complexos

// Template de email
{{ `
Olá ${$vars.userName},

Seu pedido #${$trigger.body.orderId} foi confirmado.

Total: R$ ${$trigger.body.total.toFixed(2)}
Previsão de entrega: ${new Date($trigger.body.deliveryDate).toLocaleDateString('pt-BR')}

Obrigado pela preferência!
`.trim() }}

Regex

// Validar formato
{{ /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/.test($vars.email) }}

// Extrair números
{{ $vars.text.match(/\d+/g) }}

// Substituir múltiplos espaços
{{ $vars.text.replace(/\s+/g, ' ') }}

// Extrair domínio de email
{{ $vars.email.match(/@(.+)$/)?.[1] }}

Formatação

// CPF
{{
  $vars.cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4')
}}

// Telefone
{{
  $vars.phone.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3')
}}

// CEP
{{
  $vars.cep.replace(/(\d{5})(\d{3})/, '$1-$2')
}}

Manipulação de Datas

Cálculos de Data

// Adicionar dias
{{
  new Date(Date.now() + (7 * 24 * 60 * 60 * 1000)).toISOString()
}}

// Diferença em dias
{{
  Math.floor(
    (new Date($vars.endDate) - new Date($vars.startDate)) /
    (1000 * 60 * 60 * 24)
  )
}}

// Primeiro dia do mês
{{
  new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString()
}}

// Último dia do mês
{{
  new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).toISOString()
}}

Formatação de Data

// Brasileiro
{{
  new Date($vars.date).toLocaleDateString('pt-BR', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric'
  })
}}

// Data e hora completa
{{
  new Date($vars.date).toLocaleString('pt-BR', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
  })
}}

// Relativo
{{
  (() => {
    const diff = Date.now() - new Date($vars.date).getTime();
    const minutes = Math.floor(diff / 60000);
    const hours = Math.floor(diff / 3600000);
    const days = Math.floor(diff / 86400000);

    if (minutes < 1) return 'agora';
    if (minutes < 60) return `${minutes}min atrás`;
    if (hours < 24) return `${hours}h atrás`;
    return `${days}d atrás`;
  })()
}}

Manipulação de Arrays

Métodos Avançados

// Every - todos atendem a condição
{{ $node.output.users.every(u => u.age >= 18) }}

// Some - pelo menos um atende a condição
{{ $node.output.permissions.some(p => p === 'admin') }}

// Flat - achatar arrays aninhados
{{ $node.output.nestedArray.flat(2) }}

// FlatMap - map + flat
{{ $node.output.users.flatMap(u => u.emails) }}

Transformações Complexas

// Criar lookup object
{{
  $node.output.users.reduce((acc, user) => {
    acc[user.id] = user;
    return acc;
  }, {})
}}

// Pivot de dados
{{
  $node.output.sales.reduce((acc, sale) => {
    const month = new Date(sale.date).getMonth();
    acc[month] = (acc[month] || 0) + sale.amount;
    return acc;
  }, {})
}}

Manipulação de Objetos

Transformações

// Selecionar campos específicos
{{
  Object.fromEntries(
    Object.entries($node.output.user)
      .filter(([key]) => ['id', 'name', 'email'].includes(key))
  )
}}

// Renomear chaves
{{
  Object.fromEntries(
    Object.entries($node.output.data)
      .map(([key, value]) => [key.toLowerCase(), value])
  )
}}

// Merge profundo
{{
  (() => {
    const merge = (target, source) => {
      for (const key in source) {
        if (source[key] instanceof Object && key in target) {
          Object.assign(source[key], merge(target[key], source[key]));
        }
      }
      return Object.assign(target || {}, source);
    };
    return merge($vars.defaults, $trigger.body);
  })()
}}

Validações Avançadas

Validação de Schema

// Validar estrutura de objeto
{{
  (() => {
    const data = $trigger.body;
    return (
      typeof data.name === 'string' &&
      typeof data.email === 'string' &&
      typeof data.age === 'number' &&
      data.age > 0 &&
      /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)
    );
  })()
}}

Validação de Lista

// Validar todos os itens
{{
  $node.output.items.length > 0 &&
  $node.output.items.every(item =>
    item.id &&
    item.price > 0 &&
    item.quantity > 0
  )
}}

Performance e Otimização

Evitar Cálculos Redundantes

Não otimizado:

{{ $node.output.items.length > 0 ? $node.output.items.length : 0 }}

Otimizado:

{{ $node.output.items.length || 0 }}

Cache de Valores

Use variáveis intermediárias para valores reutilizados:

// Armazene em variável para reutilizar
{{
  (() => {
    const items = $node.output.items.filter(i => i.active);
    return {
      count: items.length,
      total: items.reduce((sum, i) => sum + i.price, 0),
      items: items
    };
  })()
}}

Exemplos Práticos Completos

Exemplo 1: Formatador de Endereço

{{
  (() => {
    const addr = $trigger.body.address;
    return `${addr.street}, ${addr.number}${addr.complement ? ' - ' + addr.complement : ''}
${addr.neighborhood} - ${addr.city}/${addr.state}
CEP: ${addr.zipCode.replace(/(\d{5})(\d{3})/, '$1-$2')}`;
  })()
}}

Exemplo 2: Calculadora de Carrinho

{{
  (() => {
    const items = $node.output.cartItems;
    const subtotal = items.reduce((sum, i) => sum + (i.price * i.quantity), 0);
    const discount = $vars.discountPercent ? subtotal * ($vars.discountPercent / 100) : 0;
    const shipping = subtotal > 100 ? 0 : 15.00;
    const total = subtotal - discount + shipping;

    return {
      subtotal: subtotal.toFixed(2),
      discount: discount.toFixed(2),
      shipping: shipping.toFixed(2),
      total: total.toFixed(2),
      itemCount: items.reduce((sum, i) => sum + i.quantity, 0)
    };
  })()
}}

Exemplo 3: Gerador de Relatório

{{
  (() => {
    const sales = $node.output.salesData;
    const byCategory = sales.reduce((acc, sale) => {
      acc[sale.category] = (acc[sale.category] || 0) + sale.amount;
      return acc;
    }, {});

    const topCategory = Object.entries(byCategory)
      .sort(([,a], [,b]) => b - a)[0];

    return {
      totalSales: sales.length,
      totalRevenue: sales.reduce((sum, s) => sum + s.amount, 0).toFixed(2),
      averageTicket: (sales.reduce((sum, s) => sum + s.amount, 0) / sales.length).toFixed(2),
      topCategory: topCategory[0],
      topCategoryRevenue: topCategory[1].toFixed(2),
      categories: byCategory
    };
  })()
}}

Tratamento de Erros em Expressões

Try-Catch

{{
  (() => {
    try {
      return JSON.parse($trigger.body.data);
    } catch (e) {
      return { error: 'Invalid JSON', message: e.message };
    }
  })()
}}

Validação Defensiva

{{
  $node.output?.data?.items?.length > 0
    ? $node.output.data.items[0].value
    : null
}}

Boas Práticas

1. Mantenha Expressões Legíveis

Difícil de ler:

{{$node.output.items.filter(i=>i.price>100&&i.stock>0).map(i=>({id:i.id,total:i.price*1.1}))}}

Legível:

{{
  $node.output.items
    .filter(i => i.price > 100 && i.stock > 0)
    .map(i => ({
      id: i.id,
      total: i.price * 1.1
    }))
}}

2. Use IIFE para Lógica Complexa

{{
  (() => {
    // Lógica multi-step clara
    const data = $node.output.rawData;
    const filtered = data.filter(/* ... */);
    const transformed = filtered.map(/* ... */);
    return transformed;
  })()
}}

3. Documente Expressões Complexas

Use nós de comentário ou descrições para explicar expressões não óbvias.

Próximos Passos


← Variáveis | Tutoriais →