Skip to content

Tratamento de Erros

O SDK TinyERP usa uma classe de erro customizada (TinyApiError) que preserva todos os detalhes retornados pela API.

TinyApiError

Propriedades

typescript
class TinyApiError extends Error {
  codigo: string;                    // Código do erro
  statusProcessamento: string;       // "OK", "Erro", "Processamento"
  erros: Array<{ erro: string }>;   // Array de erros detalhados
}

Capturando Erros

Exemplo Básico

typescript
import { TinyApiError } from 'sdk-tinyerp';

try {
  const product = await sdk.product.getById(999999);
} catch (error) {
  if (error instanceof TinyApiError) {
    console.error('Código:', error.codigo);
    console.error('Status:', error.statusProcessamento);
    console.error('Mensagem:', error.message);

    // Erros detalhados
    error.erros.forEach(e => {
      console.error('  -', e.erro);
    });
  } else {
    console.error('Erro desconhecido:', error);
  }
}

Erros Comuns

Token Inválido

typescript
try {
  const info = await sdk.account.getInfo();
} catch (error) {
  if (error instanceof TinyApiError) {
    if (error.message.includes('token') || error.codigo === '401') {
      console.error('❌ Token inválido ou expirado');
      // Notificar admin, tentar renovar token, etc.
    }
  }
}

Registro Não Encontrado

typescript
try {
  const contact = await sdk.contact.getById(123456);
} catch (error) {
  if (error instanceof TinyApiError) {
    if (error.message.includes('não encontrado')) {
      console.log('Contato não existe');
      return null;
    }
  }
  throw error;
}

Erro de Validação

typescript
try {
  const result = await sdk.contact.create([{
    sequencia: 1,
    contato: {
      nome: 'João',
      tipo_pessoa: 'F'
      // Falta CPF obrigatório
    }
  }]);
} catch (error) {
  if (error instanceof TinyApiError) {
    console.error('Erros de validação:');
    error.erros.forEach(e => console.error(`  - ${e.erro}`));
  }
}

Retry em Caso de Falha

typescript
async function fetchWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof TinyApiError) {
        // Não fazer retry em erros de validação
        if (error.statusProcessamento === 'Erro') {
          throw error;
        }
      }

      if (attempt === maxRetries) {
        throw error;
      }

      console.log(`Tentativa ${attempt} falhou, tentando novamente em ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      delay *= 2; // Backoff exponencial
    }
  }
  throw new Error('Não deveria chegar aqui');
}

// Uso
const products = await fetchWithRetry(() => sdk.product.search('notebook'));

Erros em Operações em Lote

typescript
const results = await sdk.product.create([
  { sequencia: 1, data: { /* produto 1 */ } },
  { sequencia: 2, data: { /* produto 2 */ } },
  { sequencia: 3, data: { /* produto 3 */ } }
]);

// Separar sucessos e erros
const sucessos = results.filter(r => r.status === 'OK');
const erros = results.filter(r => r.status === 'Erro');

console.log(`✅ ${sucessos.length} criados`);
console.log(`❌ ${erros.length} com erro`);

// Processar erros
if (erros.length > 0) {
  console.error('\nErros encontrados:');
  erros.forEach(erro => {
    console.error(`\nSequência ${erro.sequencia}:`);
    erro.erros?.forEach(e => {
      console.error(`  - ${e.erro}`);
    });
  });

  // Opcional: tentar criar novamente apenas os que falharam
  const retry = erros.map(e => {
    const original = results.find(r => r.sequencia === e.sequencia);
    return original;
  }).filter(Boolean);

  // Corrigir problemas e tentar novamente...
}

Logging de Erros

typescript
class ErrorLogger {
  static log(error: unknown, context: string) {
    const timestamp = new Date().toISOString();

    if (error instanceof TinyApiError) {
      console.error(`[${timestamp}] [${context}] TinyApiError:`, {
        codigo: error.codigo,
        status: error.statusProcessamento,
        message: error.message,
        erros: error.erros
      });

      // Enviar para serviço de monitoramento (Sentry, etc.)
      // Sentry.captureException(error, { extra: { context } });
    } else {
      console.error(`[${timestamp}] [${context}] UnknownError:`, error);
    }
  }
}

// Uso
try {
  await sdk.product.create([...]);
} catch (error) {
  ErrorLogger.log(error, 'ProductCreation');
  throw error;
}

Validação Preventiva

typescript
function validateContact(contact: any): string[] {
  const errors: string[] = [];

  if (!contact.nome || contact.nome.length < 3) {
    errors.push('Nome deve ter no mínimo 3 caracteres');
  }

  if (!['F', 'J'].includes(contact.tipo_pessoa)) {
    errors.push('Tipo de pessoa inválido (F ou J)');
  }

  if (contact.tipo_pessoa === 'F' && (!contact.cpf_cnpj || contact.cpf_cnpj.length !== 11)) {
    errors.push('CPF inválido para Pessoa Física');
  }

  if (contact.email && !contact.email.includes('@')) {
    errors.push('Email inválido');
  }

  return errors;
}

// Uso
const validationErrors = validateContact(newContact);
if (validationErrors.length > 0) {
  console.error('Erros de validação antes de enviar:');
  validationErrors.forEach(e => console.error(`  - ${e}`));
  return;
}

// Agora sim, criar o contato
const result = await sdk.contact.create([{ sequencia: 1, contato: newContact }]);

Wrapper de Erro Customizado

typescript
class SDKErrorHandler {
  static async execute<T>(
    fn: () => Promise<T>,
    errorMessage: string
  ): Promise<T | null> {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof TinyApiError) {
        console.error(`${errorMessage}:`, error.message);
        console.error('Detalhes:', error.erros.map(e => e.erro).join(', '));
        return null;
      }
      throw error;
    }
  }
}

// Uso
const product = await SDKErrorHandler.execute(
  () => sdk.product.getById(123),
  'Erro ao buscar produto'
);

if (!product) {
  console.log('Produto não encontrado ou erro na busca');
}

Próximos Passos

SDK não oficial para TinyERP/Olist