> ## Documentation Index
> Fetch the complete documentation index at: https://docs.triglit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Node Registry

> Entenda como o Node Registry fornece schemas completos de nodes disponíveis para construção de editores de workflows

O **Node Registry** é um endpoint da API pública que fornece um schema completo e estruturado de todos os nodes disponíveis (built-in e customizados) para um tenant. Ele é fundamental para a construção de editores de workflows no frontend, fornecendo todas as informações necessárias para renderizar, validar e configurar nodes dinamicamente.

## Propósito

O Node Registry serve como uma **fonte única de verdade** sobre os nodes disponíveis, fornecendo:

* **Catálogo completo**: Lista todos os nodes built-in e customizados disponíveis para o tenant
* **Schemas detalhados**: Define estruturas de entrada, saída e configuração de cada node
* **Metadados**: Informações sobre versão, descrição, capacidades e tipo de cada node
* **Validação**: Especificações que permitem validação em tempo real no editor

## Endpoint

```http theme={null}
GET /v1/gateway/custom-nodes/registry
```

### Autenticação

Requer autenticação via chave de API (pública ou secreta):

```http theme={null}
X-API-Key: pk_sua_chave_aqui
```

### Resposta

```json theme={null}
{
  "nodes": [
    {
      "type": "input",
      "name": "User Input",
      "description": "Waits for user input and pauses workflow execution",
      "version": "1.0.0",
      "isBuiltIn": true,
      "inputSchema": {
        "data": {
          "type": "any",
          "description": "Input data to process",
          "required": true
        }
      },
      "outputSchema": {
        "result": {
          "type": "object",
          "description": "Processing result",
          "properties": {
            "success": { "type": "boolean" },
            "data": { "type": "any" }
          }
        }
      },
      "configSchema": {
        "timeout": {
          "type": "number",
          "description": "Timeout in milliseconds",
          "min": 1000,
          "max": 300000,
          "default": 30000
        }
      },
      "canPause": true
    },
    {
      "type": "custom-api-call",
      "name": "Custom API Call",
      "description": "Custom node example",
      "version": "1.0.0",
      "isBuiltIn": false,
      "inputSchema": {
        "data": {
          "type": "any",
          "description": "Input data to process",
          "required": true
        }
      },
      "outputSchema": {
        "result": {
          "type": "object",
          "description": "API call result"
        }
      },
      "configSchema": {
        "endpoint": {
          "type": "string",
          "description": "API endpoint URL",
          "required": true
        },
        "method": {
          "type": "enum",
          "enum": ["GET", "POST", "PUT", "DELETE"],
          "default": "POST"
        }
      },
      "canPause": false
    }
  ],
  "total": 7
}
```

## Estrutura do Schema

Cada node no registry contém as seguintes propriedades:

### Propriedades Principais

<ParamField body="type" type="string" required>
  Identificador único do tipo de node (ex: `"input"`, `"transform"`, `"custom-api-call"`). Usado para referenciar o node ao criar workflows.
</ParamField>

<ParamField body="name" type="string" required>
  Nome legível do node exibido na interface do usuário (ex: `"User Input"`, `"Transform Data"`).
</ParamField>

<ParamField body="description" type="string">
  Descrição detalhada do que o node faz e quando deve ser usado.
</ParamField>

<ParamField body="version" type="string" required>
  Versão do node seguindo semantic versioning (semver), ex: `"1.0.0"`.
</ParamField>

<ParamField body="isBuiltIn" type="boolean" required>
  Indica se é um node built-in (`true`) ou customizado (`false`). Nodes built-in são fornecidos pela plataforma, enquanto customizados são criados pelo tenant.
</ParamField>

<ParamField body="canPause" type="boolean" required>
  Indica se o node pode pausar a execução do workflow (ex: aguardando input do usuário). Nodes com `canPause: true` permitem workflows interativos.
</ParamField>

### Schemas de Dados

<ParamField body="inputSchema" type="object" required>
  Schema que define os campos de entrada esperados pelo node. Cada chave é o nome do campo e o valor é um `NodeFieldSchemaDto` com especificação completa de tipo, validação e descrição.

  **Exemplo:**

  ```json theme={null}
  {
    "data": {
      "type": "string",
      "description": "Data to process",
      "required": true,
      "minLength": 1,
      "maxLength": 1000
    },
    "options": {
      "type": "object",
      "properties": {
        "format": {
          "type": "enum",
          "enum": ["json", "xml", "csv"],
          "default": "json"
        }
      }
    }
  }
  ```
</ParamField>

<ParamField body="outputSchema" type="object" required>
  Schema que define os campos de saída produzidos pelo node. Estrutura similar ao `inputSchema`, mas descreve o que o node retorna após a execução.

  **Exemplo:**

  ```json theme={null}
  {
    "result": {
      "type": "object",
      "description": "Processing result",
      "properties": {
        "success": { "type": "boolean" },
        "data": { "type": "any" },
        "metadata": {
          "type": "object",
          "properties": {
            "processedAt": { "type": "string" },
            "duration": { "type": "number" }
          }
        }
      }
    }
  }
  ```
</ParamField>

<ParamField body="configSchema" type="object">
  Schema que define as configurações do node (settings que o usuário pode ajustar ao adicionar o node ao workflow). Inclui validações, valores padrão e tipos completos.

  **Exemplo:**

  ```json theme={null}
  {
    "timeout": {
      "type": "number",
      "description": "Timeout in milliseconds",
      "min": 1000,
      "max": 300000,
      "default": 30000,
      "integer": true
    },
    "retryOnError": {
      "type": "boolean",
      "description": "Whether to retry on error",
      "default": false
    },
    "maxRetries": {
      "type": "number",
      "description": "Maximum number of retries",
      "min": 0,
      "max": 5,
      "default": 3,
      "integer": true
    }
  }
  ```
</ParamField>

## NodeFieldSchemaDto

O `NodeFieldSchemaDto` é a estrutura base usada em `inputSchema`, `outputSchema` e `configSchema`. Ele suporta tipos complexos e validações avançadas:

### Tipos Suportados

* **`string`**: Texto com validações de comprimento e padrões regex
* **`number`**: Números com validações de min/max e suporte a inteiros
* **`boolean`**: Valores booleanos
* **`any`**: Qualquer tipo (sem validação específica)
* **`object`**: Objetos com propriedades definidas e suporte a propriedades adicionais
* **`array`**: Arrays com schema de itens e validações de tamanho
* **`enum`**: Valores enumerados (lista de valores permitidos)

### Propriedades Comuns

<ParamField body="type" type="string" required>
  Tipo do campo: `"string"`, `"number"`, `"boolean"`, `"any"`, `"object"`, `"array"` ou `"enum"`.
</ParamField>

<ParamField body="description" type="string">
  Descrição do propósito do campo.
</ParamField>

<ParamField body="required" type="boolean">
  Se o campo é obrigatório (padrão: `false`).
</ParamField>

<ParamField body="default" type="any">
  Valor padrão do campo se não fornecido.
</ParamField>

### Validações por Tipo

#### String

<ParamField body="minLength" type="number">
  Comprimento mínimo (inclusivo).
</ParamField>

<ParamField body="maxLength" type="number">
  Comprimento máximo (inclusivo).
</ParamField>

<ParamField body="pattern" type="string">
  Padrão regex para validação.
</ParamField>

#### Number

<ParamField body="min" type="number">
  Valor mínimo (inclusivo).
</ParamField>

<ParamField body="max" type="number">
  Valor máximo (inclusivo).
</ParamField>

<ParamField body="integer" type="boolean">
  Se o número deve ser inteiro (sem decimais).
</ParamField>

#### Object

<ParamField body="properties" type="object">
  Definição de propriedades do objeto (recursivo, cada propriedade é um `NodeFieldSchemaDto`).
</ParamField>

<ParamField body="additionalProperties" type="boolean">
  Se propriedades adicionais (não definidas em `properties`) são permitidas.
</ParamField>

#### Array

<ParamField body="items" type="NodeFieldSchemaDto">
  Schema dos itens do array (recursivo).
</ParamField>

<ParamField body="minItems" type="number">
  Número mínimo de itens (inclusivo).
</ParamField>

<ParamField body="maxItems" type="number">
  Número máximo de itens (inclusivo).
</ParamField>

#### Enum

<ParamField body="enum" type="array">
  Lista de valores permitidos: `["value1", "value2", ...]`.
</ParamField>

## Padrões e Protocolo

### Multi-Tenancy

O Node Registry é **isolado por tenant**. Cada requisição retorna apenas os nodes disponíveis para o tenant autenticado:

* **Nodes built-in**: Fornecidos pela plataforma, podem ser desabilitados por tenant via configuração
* **Nodes customizados**: Criados pelo tenant e visíveis apenas para ele
* **Traduções**: Aplicadas automaticamente baseadas na configuração de i18n do tenant

### Versionamento

* Nodes seguem **semantic versioning (semver)**
* Versões são imutáveis: uma vez criado, um node mantém sua versão
* Novas versões devem ser criadas como novos nodes ou através de atualizações controladas

### Filtragem e Personalização

O registry aplica automaticamente:

1. **Filtragem de built-in nodes**: Remove nodes built-in desabilitados na configuração do tenant
2. **Traduções**: Aplica traduções baseadas no idioma configurado do tenant
3. **Ativação**: Inclui apenas nodes customizados ativos (`isActive: true`)

### Ordenação

A ordem dos nodes na resposta não é garantida. O frontend deve ordenar conforme necessário (ex: por tipo, nome, ou categoria).

## Uso no Frontend

O Node Registry é projetado para ser consumido por editores de workflows no frontend. Recomendamos usar o **SDK oficial do Triglit** para facilitar a integração.

### Instalação do SDK

```bash theme={null}
npm install triglit
```

Aqui está um exemplo de como utilizá-lo:

### Exemplo: Construindo um Editor de Workflows

```typescript theme={null}
import Triglit from 'triglit';

// 1. Inicializar o cliente do SDK
const client = new Triglit({ 
  apiKey: 'pk_sua_chave_aqui' 
});

// 2. Buscar o registry usando o SDK
const registry = await client.customNodes.retrieveRegistry();

// 3. Renderizar lista de nodes disponíveis
function NodePalette({ registry }) {
  return (
    <div className="node-palette">
      <h3>Nodes Disponíveis</h3>
      {registry.nodes.map(node => (
        <NodeCard
          key={node.type}
          type={node.type}
          name={node.name}
          description={node.description}
          isBuiltIn={node.isBuiltIn}
          onClick={() => addNodeToWorkflow(node.type)}
        />
      ))}
    </div>
  );
}

// 4. Renderizar formulário de configuração do node
function NodeConfigForm({ nodeType, registry }) {
  const node = registry.nodes.find(n => n.type === nodeType);
  if (!node || !node.configSchema) return null;

  return (
    <form>
      {Object.entries(node.configSchema).map(([fieldName, fieldSchema]) => (
        <FieldInput
          key={fieldName}
          name={fieldName}
          schema={fieldSchema}
          // Renderiza input baseado no tipo e validações
        />
      ))}
    </form>
  );
}

// 5. Validar configuração antes de salvar
function validateNodeConfig(nodeType, config, registry) {
  const node = registry.nodes.find(n => n.type === nodeType);
  if (!node || !node.configSchema) return { valid: true };

  const errors = {};
  for (const [fieldName, fieldSchema] of Object.entries(node.configSchema)) {
    const value = config[fieldName];
    
    // Validar required
    if (fieldSchema.required && (value === undefined || value === null)) {
      errors[fieldName] = 'Campo obrigatório';
      continue;
    }

    // Validar tipo
    if (value !== undefined && !validateType(value, fieldSchema.type)) {
      errors[fieldName] = `Tipo inválido. Esperado: ${fieldSchema.type}`;
      continue;
    }

    // Validar constraints específicas do tipo
    if (fieldSchema.type === 'string') {
      if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
        errors[fieldName] = `Mínimo ${fieldSchema.minLength} caracteres`;
      }
      if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
        errors[fieldName] = `Máximo ${fieldSchema.maxLength} caracteres`;
      }
      if (fieldSchema.pattern && !new RegExp(fieldSchema.pattern).test(value)) {
        errors[fieldName] = 'Formato inválido';
      }
    }

    if (fieldSchema.type === 'number') {
      if (fieldSchema.min !== undefined && value < fieldSchema.min) {
        errors[fieldName] = `Valor mínimo: ${fieldSchema.min}`;
      }
      if (fieldSchema.max !== undefined && value > fieldSchema.max) {
        errors[fieldName] = `Valor máximo: ${fieldSchema.max}`;
      }
      if (fieldSchema.integer && !Number.isInteger(value)) {
        errors[fieldName] = 'Deve ser um número inteiro';
      }
    }

    if (fieldSchema.type === 'enum' && !fieldSchema.enum?.includes(value)) {
      errors[fieldName] = `Valor deve ser um de: ${fieldSchema.enum.join(', ')}`;
    }
  }

  return {
    valid: Object.keys(errors).length === 0,
    errors
  };
}
```

### Integração com React SDK

O React SDK do Triglit já utiliza o Node Registry internamente:

```tsx theme={null}
import { WorkflowEditor } from '@triglit/react-sdk';

function MyWorkflowEditor() {
  return (
    <WorkflowEditor
      apiKey="pk_sua_chave_aqui"
      // O SDK busca automaticamente o registry e renderiza os nodes
      onNodeAdd={(nodeType) => console.log('Node adicionado:', nodeType)}
    />
  );
}
```

## Casos de Uso

### 1. Construção Dinâmica de UI

O registry permite construir interfaces de edição de workflows **sem hardcoding** de nodes:

* Renderizar paleta de nodes baseada no registry
* Gerar formulários de configuração automaticamente
* Validar inputs em tempo real usando os schemas

### 2. Documentação Automática

Os schemas podem ser usados para gerar documentação automática:

* Listar todos os nodes disponíveis
* Mostrar exemplos de configuração
* Explicar inputs e outputs esperados

### 3. Validação de Workflows

Antes de salvar um workflow, valide se:

* Todos os nodes usados existem no registry
* Configurações estão de acordo com os schemas
* Conexões entre nodes são válidas (outputs compatíveis com inputs)

### 4. Extensibilidade

O registry facilita a extensão da plataforma:

* Novos nodes built-in aparecem automaticamente
* Nodes customizados são imediatamente disponíveis
* Sem necessidade de atualizar o frontend para novos nodes

## Boas Práticas

### Cache

<Warning>
  O registry pode mudar quando nodes customizados são criados, atualizados ou desativados. Implemente cache com invalidação apropriada.
</Warning>

Recomendações:

* Cache o registry por alguns minutos (ex: 5-10 minutos)
* Invalide o cache quando o usuário criar/editar nodes customizados
* Considere usar ETags ou versionamento para detectar mudanças

### Tratamento de Erros

```typescript theme={null}
import Triglit from 'triglit';

const client = new Triglit({ apiKey: 'pk_sua_chave_aqui' });

try {
  const registry = await client.customNodes.retrieveRegistry();
  // Usar registry
} catch (error) {
  if (error.status === 401) {
    // Chave de API inválida
  } else if (error.status === 429) {
    // Rate limit excedido
  } else {
    // Erro do servidor
  }
}
```

### Performance

* Busque o registry uma vez ao carregar o editor
* Considere paginação se o número de nodes for muito grande (futuro)
* Use lazy loading para nodes customizados se necessário

## Exemplo Completo

```typescript theme={null}
import Triglit from 'triglit';
import type { CustomNodeRetrieveRegistryResponse } from 'triglit';

// service/node-registry.service.ts
class NodeRegistryService {
  private client: Triglit;
  private cache: CustomNodeRetrieveRegistryResponse | null = null;
  private cacheExpiry: number = 0;
  private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutos

  constructor(apiKey: string) {
    this.client = new Triglit({ apiKey });
  }

  async getRegistry(forceRefresh = false): Promise<CustomNodeRetrieveRegistryResponse> {
    const now = Date.now();
    
    if (!forceRefresh && this.cache && now < this.cacheExpiry) {
      return this.cache;
    }

    try {
      this.cache = await this.client.customNodes.retrieveRegistry();
      this.cacheExpiry = now + this.CACHE_TTL;
      return this.cache;
    } catch (error) {
      throw new Error(`Failed to fetch registry: ${error.message}`);
    }
  }

  invalidateCache() {
    this.cache = null;
    this.cacheExpiry = 0;
  }

  findNode(
    type: string, 
    registry: CustomNodeRetrieveRegistryResponse
  ): CustomNodeRetrieveRegistryResponse['nodes'][0] | undefined {
    return registry.nodes.find(node => node.type === type);
  }

  validateConfig(
    nodeType: string,
    config: Record<string, unknown>,
    registry: CustomNodeRetrieveRegistryResponse
  ): { valid: boolean; errors: Record<string, string> } {
    const node = this.findNode(nodeType, registry);
    if (!node || !node.configSchema) {
      return { valid: true, errors: {} };
    }

    const errors: Record<string, string> = {};
    
    for (const [fieldName, fieldSchema] of Object.entries(node.configSchema)) {
      const value = config[fieldName];
      
      // Validar required
      if (fieldSchema.required && (value === undefined || value === null)) {
        errors[fieldName] = 'Campo obrigatório';
        continue;
      }

      // Validar tipo
      if (value !== undefined && !this.validateType(value, fieldSchema.type)) {
        errors[fieldName] = `Tipo inválido. Esperado: ${fieldSchema.type}`;
        continue;
      }

      // Validar constraints específicas do tipo
      if (fieldSchema.type === 'string' && typeof value === 'string') {
        if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
          errors[fieldName] = `Mínimo ${fieldSchema.minLength} caracteres`;
        }
        if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
          errors[fieldName] = `Máximo ${fieldSchema.maxLength} caracteres`;
        }
        if (fieldSchema.pattern && !new RegExp(fieldSchema.pattern).test(value)) {
          errors[fieldName] = 'Formato inválido';
        }
      }

      if (fieldSchema.type === 'number' && typeof value === 'number') {
        if (fieldSchema.min !== undefined && value < fieldSchema.min) {
          errors[fieldName] = `Valor mínimo: ${fieldSchema.min}`;
        }
        if (fieldSchema.max !== undefined && value > fieldSchema.max) {
          errors[fieldName] = `Valor máximo: ${fieldSchema.max}`;
        }
        if (fieldSchema.integer && !Number.isInteger(value)) {
          errors[fieldName] = 'Deve ser um número inteiro';
        }
      }

      if (fieldSchema.type === 'enum' && !fieldSchema.enum?.includes(value)) {
        errors[fieldName] = `Valor deve ser um de: ${fieldSchema.enum?.join(', ')}`;
      }
    }

    return {
      valid: Object.keys(errors).length === 0,
      errors
    };
  }

  private validateType(value: unknown, type: string): boolean {
    switch (type) {
      case 'string':
        return typeof value === 'string';
      case 'number':
        return typeof value === 'number';
      case 'boolean':
        return typeof value === 'boolean';
      case 'any':
        return true;
      case 'object':
        return typeof value === 'object' && value !== null && !Array.isArray(value);
      case 'array':
        return Array.isArray(value);
      default:
        return false;
    }
  }
}

// Uso
const service = new NodeRegistryService('pk_sua_chave_aqui');
const registry = await service.getRegistry();
const node = service.findNode('input', registry);
const validation = service.validateConfig('input', { timeout: 30000 }, registry);
```

## Referências

* [SDK TypeScript Oficial](https://github.com/Triglit/typescript-sdk) - Documentação completa do SDK
* [Guia de Custom Nodes](/guides/custom-nodes) - Aprenda a criar nodes customizados
* [Conceitos: Nodes](/concepts/nodes) - Entenda o conceito de nodes
* [Referência OpenAPI](/api-reference/openapi.json) - Especificação completa da API

<Tip>
  Use o Node Registry para construir editores de workflows dinâmicos e extensíveis, sem depender de hardcoding de nodes específicos.
</Tip>
