Guia de Migração: v2 → v3
Este guia ajuda você a migrar do SDK NFE.io v2.x para v3.0.
Visão Geral
O que há de Novo na v3?
✨ Principais Melhorias:
- TypeScript Nativo - Segurança de tipos completa e suporte a IntelliSense
- Async/Await Moderno - Sem callbacks, API limpa baseada em promises
- Zero Dependências - Usa Fetch API nativa do Node.js (Node 18+)
- Melhor Tratamento de Erros - Classes de erro tipadas com informações detalhadas
- Retry Automático - Lógica de retry com exponential backoff integrada
- ESM & CommonJS - Funciona com ambos os sistemas de módulos
- OpenAPI Types - Tipos gerados automaticamente das especificações da API
⚠️ Requisitos:
- Node.js >= 18.0.0 (anteriormente v12 na v2)
- Mudanças incompatíveis na API (veja abaixo)
Cronograma de Migração
Abordagem recomendada:
- ✅ Atualize para Node.js 18+ se necessário
- ✅ Instale v3 ao lado da v2 (mesmo nome de pacote)
- ✅ Migre um recurso por vez
- ✅ Atualize os testes
- ✅ Remova dependência da v2
Mudanças Incompatíveis
1. Nome do Pacote (INALTERADO)
# v2 e v3 usam o mesmo nome
npm install nfe-io
2. Sintaxe de Import/Require
// v2
var nfe = require('nfe-io')('sua-api-key');
// v3 (ESM)
import { NfeClient } from 'nfe-io';
const nfe = new NfeClient({ apiKey: 'sua-api-key' });
// v3 (CommonJS)
const { NfeClient } = require('nfe-io');
const nfe = new NfeClient({ apiKey: 'sua-api-key' });
3. Configuração
// v2
var nfe = require('nfe-io')('api-key');
nfe.setTimeout(60000);
// v3
const nfe = new NfeClient({
apiKey: 'api-key',
timeout: 60000,
environment: 'production', // ou 'development'
retryConfig: {
maxRetries: 3,
baseDelay: 1000
}
});
4. Callbacks → Async/Await
// v2 (callbacks)
nfe.serviceInvoices.create('company-id', data, function(err, invoice) {
if (err) return console.error(err);
console.log(invoice);
});
// v2 (promises)
nfe.serviceInvoices.create('company-id', data)
.then(invoice => console.log(invoice))
.catch(err => console.error(err));
// v3 (async/await - RECOMENDADO)
try {
const invoice = await nfe.serviceInvoices.create('company-id', data);
console.log(invoice);
} catch (error) {
console.error(error);
}
5. Tratamento de Erros
// v2
nfe.serviceInvoices.create('company-id', data, function(err, invoice) {
if (err) {
if (err.type === 'AuthenticationError') {
// tratar erro de autenticação
}
}
});
// v3
import { AuthenticationError, ValidationError } from 'nfe-io';
try {
const invoice = await nfe.serviceInvoices.create('company-id', data);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('API key inválida');
} else if (error instanceof ValidationError) {
console.error('Dados inválidos:', error.details);
}
}
6. Formato de Resposta
// v2 - Retorno direto dos dados
const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
console.log(invoice.number);
// v3 - Igual! (sem mudanças)
const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
console.log(invoice.number);
7. Mudanças nos Nomes dos Métodos
| Método v2 | Método v3 | Notas |
|---|---|---|
create() | create() | ✅ Igual |
list() | list() | ✅ Igual |
retrieve() | retrieve() | ✅ Igual |
update() | update() | ✅ Igual |
delete() | delete() / remove() | ⚠️ remove() em Companies |
sendEmail() | sendEmail() | ✅ Igual |
downloadPdf() | downloadPdf() | ✅ Igual |
downloadXml() | downloadXml() | ✅ Igual |
| N/A | createAndWait() | 🆕 Novo! Polling automático |
| N/A | listAll() | 🆕 Paginação automática |
| N/A | findByTaxNumber() | 🆕 Busca por CNPJ/CPF |
Migração Passo a Passo
Passo 1: Instalar v3
# Instalar novo pacote (v2 fica instalada por enquanto)
npm install [email protected]
# Verificar versão do Node.js
node --version # Deve ser >= 18.0.0
Passo 2: Atualizar Imports
- var nfe = require('nfe-io')('api-key');
+ const { NfeClient } = require('nfe-io');
+ const nfe = new NfeClient({ apiKey: 'api-key' });
Ou com ES Modules:
+ import { NfeClient } from 'nfe-io';
+ const nfe = new NfeClient({ apiKey: 'api-key' });
Passo 3: Converter Callbacks para Async/Await
- nfe.serviceInvoices.create('company-id', data, function(err, invoice) {
- if (err) return console.error(err);
- console.log(invoice);
- });
+ async function criarNotaFiscal() {
+ try {
+ const invoice = await nfe.serviceInvoices.create('company-id', data);
+ console.log(invoice);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ criarNotaFiscal();
Passo 4: Atualizar Tratamento de Erros
+ import {
+ NfeError,
+ AuthenticationError,
+ ValidationError,
+ NotFoundError
+ } from 'nfe-io';
try {
const invoice = await nfe.serviceInvoices.create('company-id', data);
} catch (error) {
- if (error.type === 'AuthenticationError') {
+ if (error instanceof AuthenticationError) {
console.error('Autenticação falhou');
}
- if (error.type === 'ValidationError') {
+ if (error instanceof ValidationError) {
console.error('Dados inválidos:', error.details);
}
}
Passo 5: Atualizar TypeScript (se aplicável)
// Adicionar tipos ao seu código
import { NfeClient, ServiceInvoice, Company } from 'nfe-io';
const nfe = new NfeClient({ apiKey: 'api-key' });
async function getInvoice(
companyId: string,
invoiceId: string
): Promise<ServiceInvoice> {
return await nfe.serviceInvoices.retrieve(companyId, invoiceId);
}
Passo 6: Remover v2
# Após todo código migrado e testado
# Não há necessidade de desinstalar se estiver na mesma major version
# Apenas atualize suas importações e uso do código
Mudanças na API
Notas Fiscais de Serviço (Service Invoices)
// v2
nfe.serviceInvoices.create('company-id', invoiceData, callback);
nfe.serviceInvoices.list('company-id', callback);
nfe.serviceInvoices.retrieve('company-id', 'invoice-id', callback);
nfe.serviceInvoices.cancel('company-id', 'invoice-id', callback);
nfe.serviceInvoices.sendEmail('company-id', 'invoice-id', callback);
nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id', callback);
nfe.serviceInvoices.downloadXml('company-id', 'invoice-id', callback);
// v3
await nfe.serviceInvoices.create('company-id', invoiceData);
await nfe.serviceInvoices.list('company-id', { pageCount: 50, pageIndex: 0 });
await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
await nfe.serviceInvoices.cancel('company-id', 'invoice-id');
await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id');
await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id');
await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id');
// 🆕 Novo na v3: Polling automático para processamento assíncrono
await nfe.serviceInvoices.createAndWait('company-id', invoiceData, {
maxAttempts: 30,
intervalMs: 2000
});
Empresas (Companies)
// v2
nfe.companies.create(companyData, callback);
nfe.companies.list(callback);
nfe.companies.retrieve('company-id', callback);
nfe.companies.update('company-id', updates, callback);
nfe.companies.uploadCertificate('company-id', fileData, password, callback);
// v3 - CRUD Básico (mesmo padrão, agora async)
await nfe.companies.create(companyData);
await nfe.companies.list({ pageCount: 20, pageIndex: 0 });
await nfe.companies.retrieve('company-id');
await nfe.companies.update('company-id', updates);
await nfe.companies.remove('company-id'); // Renomeado de 'delete'
// v3 - Gerenciamento de Certificados (aprimorado)
await nfe.companies.uploadCertificate('company-id', {
file: fileBuffer,
password: 'senha-certificado',
filename: 'certificate.pfx' // Opcional
});
// 🆕 Novo na v3: Utilitários de certificado
const validation = await nfe.companies.validateCertificate(certBuffer, 'senha');
const status = await nfe.companies.getCertificateStatus('company-id');
const warning = await nfe.companies.checkCertificateExpiration('company-id', 30);
// 🆕 Novo na v3: Helpers de paginação
const allCompanies = await nfe.companies.listAll(); // Paginação automática
for await (const company of nfe.companies.listIterator()) {
// Streaming eficiente de memória
}
// 🆕 Novo na v3: Métodos de busca
const company = await nfe.companies.findByTaxNumber(12345678000190); // CNPJ
const matches = await nfe.companies.findByName('Acme'); // Por nome
const withCerts = await nfe.companies.getCompaniesWithCertificates();
const expiring = await nfe.companies.getCompaniesWithExpiringCertificates(30);
Principais Mudanças:
- ✅
delete()→remove()(evita palavra reservada JavaScript) - ✅
uploadCertificate()agora recebe objeto com{ file, password, filename? } - 🆕 Validação de certificado antes do upload
- 🆕 Monitoramento de expiração de certificados
- 🆕 Busca por CNPJ/CPF ou nome
- 🆕 Paginação automática com
listAll()elistIterator()
Pessoas Jurídicas e Físicas (Legal People & Natural People)
// v2
nfe.legalPeople.create('company-id', personData, callback);
nfe.legalPeople.list('company-id', callback);
nfe.legalPeople.retrieve('company-id', 'person-id', callback);
nfe.legalPeople.update('company-id', 'person-id', updates, callback);
nfe.legalPeople.delete('company-id', 'person-id', callback);
// v3 (mesmo padrão, apenas async)
await nfe.legalPeople.create('company-id', personData);
await nfe.legalPeople.list('company-id');
await nfe.legalPeople.retrieve('company-id', 'person-id');
await nfe.legalPeople.update('company-id', 'person-id', updates);
await nfe.legalPeople.delete('company-id', 'person-id');
// Mesmo para pessoas físicas
await nfe.naturalPeople.create('company-id', personData);
await nfe.naturalPeople.list('company-id');
await nfe.naturalPeople.retrieve('company-id', 'person-id');
await nfe.naturalPeople.update('company-id', 'person-id', updates);
await nfe.naturalPeople.delete('company-id', 'person-id');
Mudanças:
- ✅ Validação automática de CNPJ (pessoas jurídicas)
- ✅ Validação automática de CPF (pessoas físicas)
- ✅ Mesma interface async/await para ambos os recursos
Webhooks
// v2
nfe.webhooks.create(webhookData, callback);
nfe.webhooks.list(callback);
nfe.webhooks.retrieve('webhook-id', callback);
nfe.webhooks.update('webhook-id', updates, callback);
nfe.webhooks.delete('webhook-id', callback);
// v3
await nfe.webhooks.create('company-id', webhookData);
await nfe.webhooks.list('company-id');
await nfe.webhooks.retrieve('company-id', 'webhook-id');
await nfe.webhooks.update('company-id', 'webhook-id', updates);
await nfe.webhooks.delete('company-id', 'webhook-id');
Exemplos de Código
Antes & Depois: Fluxo Completo de Emissão de Nota Fiscal
Código v2:
var nfe = require('nfe-io')('api-key');
function emitirNotaFiscal(companyId, invoiceData, callback) {
nfe.serviceInvoices.create(companyId, invoiceData, function(err, invoice) {
if (err) return callback(err);
if (invoice.code === 202) {
// Poll manual
var checkInterval = setInterval(function() {
nfe.serviceInvoices.retrieve(companyId, invoice.id, function(err, result) {
if (err) {
clearInterval(checkInterval);
return callback(err);
}
if (result.status === 'issued') {
clearInterval(checkInterval);
// Enviar email
nfe.serviceInvoices.sendEmail(companyId, result.id, function(err) {
if (err) return callback(err);
callback(null, result);
});
}
});
}, 2000);
} else {
// Enviar email
nfe.serviceInvoices.sendEmail(companyId, invoice.id, function(err) {
if (err) return callback(err);
callback(null, invoice);
});
}
});
}
emitirNotaFiscal('company-id', invoiceData, function(err, invoice) {
if (err) return console.error(err);
console.log('Nota fiscal emitida:', invoice.number);
});
Código v3:
import { NfeClient } from 'nfe-io';
const nfe = new NfeClient({ apiKey: 'api-key' });
async function emitirNotaFiscal(companyId, invoiceData) {
// Automaticamente faz polling e envia email
const invoice = await nfe.serviceInvoices.createAndWait(
companyId,
invoiceData,
{ maxAttempts: 30, intervalMs: 2000 }
);
await nfe.serviceInvoices.sendEmail(companyId, invoice.id);
return invoice;
}
// Uso
try {
const invoice = await emitirNotaFiscal('company-id', invoiceData);
console.log('Nota fiscal emitida:', invoice.number);
} catch (error) {
console.error('Falha ao emitir nota fiscal:', error);
}
Antes & Depois: Tratamento de Erros
Código v2:
nfe.serviceInvoices.create('company-id', data, function(err, invoice) {
if (err) {
if (err.type === 'AuthenticationError') {
console.error('API key inválida');
} else if (err.type === 'BadRequestError') {
console.error('Dados inválidos:', err.message);
} else {
console.error('Erro desconhecido:', err);
}
return;
}
console.log('Sucesso:', invoice);
});
Código v3:
import {
AuthenticationError,
ValidationError,
RateLimitError
} from 'nfe-io';
try {
const invoice = await nfe.serviceInvoices.create('company-id', data);
console.log('Sucesso:', invoice);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('API key inválida');
} else if (error instanceof ValidationError) {
console.error('Dados inválidos:', error.details);
} else if (error instanceof RateLimitError) {
console.error('Limite de taxa atingido, tentar após:', error.retryAfter);
} else {
console.error('Erro desconhecido:', error);
}
}
Antes & Depois: Operações em Lote
Código v2:
var async = require('async');
async.mapLimit(invoices, 5, function(invoiceData, callback) {
nfe.serviceInvoices.create('company-id', invoiceData, callback);
}, function(err, results) {
if (err) return console.error(err);
console.log('Criados:', results.length);
});
Código v3:
// Não precisa de dependências externas!
async function criarEmLote(companyId, invoices) {
const results = await Promise.allSettled(
invoices.map(data =>
nfe.serviceInvoices.create(companyId, data)
)
);
const succeeded = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');
console.log(`✅ ${succeeded.length} com sucesso`);
console.log(`❌ ${failed.length} falharam`);
return { succeeded, failed };
}
Migração de Gerenciamento de Certificados
O gerenciamento de certificados no v3 foi significativamente aprimorado:
Abordagem v2:
// v2: Upload e esperar que funcione
const fs = require('fs');
const certBuffer = fs.readFileSync('./certificate.pfx');
nfe.companies.uploadCertificate('company-id', certBuffer, 'senha', (err, result) => {
if (err) {
console.error('Upload falhou:', err);
return;
}
console.log('Certificado carregado');
});
Abordagem v3 (com validação):
// v3: Validar antes do upload
import { readFile } from 'fs/promises';
import { CertificateValidator } from 'nfe-io';
const certBuffer = await readFile('./certificate.pfx');
// 1. Verificar formato do arquivo
if (!CertificateValidator.isSupportedFormat('certificate.pfx')) {
throw new Error('Apenas arquivos .pfx e .p12 são suportados');
}
// 2. Validar certificado
const validation = await nfe.companies.validateCertificate(certBuffer, 'senha');
if (!validation.valid) {
throw new Error(`Certificado inválido: ${validation.error}`);
}
console.log('Certificado expira em:', validation.metadata?.validTo);
// 3. Upload (também valida automaticamente)
const result = await nfe.companies.uploadCertificate('company-id', {
file: certBuffer,
password: 'senha',
filename: 'certificate.pfx'
});
console.log(result.message);
Monitoramento v3:
// Configurar monitoramento de certificados expirando
async function verificarCertificados() {
const expirando = await nfe.companies.getCompaniesWithExpiringCertificates(30);
for (const company of expirando) {
const alerta = await nfe.companies.checkCertificateExpiration(company.id, 30);
if (alerta) {
console.warn(`⚠️ ${company.name}`);
console.warn(` Certificado expira em ${alerta.daysRemaining} dias`);
console.warn(` Data de expiração: ${alerta.expiresOn.toLocaleDateString()}`);
// Enviar alerta ao administrador
await enviarAlertaAdmin({
empresa: company.name,
diasRestantes: alerta.daysRemaining
});
}
}
}
// Executar diariamente
setInterval(verificarCertificados, 24 * 60 * 60 * 1000);
Perguntas Frequentes (FAQ)
P: Posso usar v2 e v3 juntos durante a migração?
R: Sim! Eles usam nomes de pacote diferentes (nfe-io v2 vs nfe-io v3), mas você pode identificá-los pela versão.
// v2 (versão 2.x.x)
const nfeV2 = require('nfe-io')('api-key');
// v3 (versão 3.x.x)
const { NfeClient } = require('nfe-io');
const nfeV3 = new NfeClient({ apiKey: 'api-key' });
P: Preciso alterar minha API key?
R: Não! Sua API key existente funciona tanto com v2 quanto com v3.
P: E se eu ainda estiver no Node.js 16?
R: Você deve atualizar para Node.js 18+ para usar v3. Considere:
- Atualizar Node.js (recomendado)
- Permanecer no v2 até poder atualizar
- Usar Node Version Manager (nvm) para testar v3
P: Há mudanças no formato de dados?
R: Não! Os formatos de request/response da API são os mesmos. Apenas a interface do SDK mudou.
P: O que acontece com meu código v2 após a migração?
R: Mantenha-o até que você tenha migrado e testado completamente. Depois, atualize para a versão 3.x.x.
P: Há diferença de desempenho?
R: Sim! v3 é mais rápido:
- Sem dependências externas = inicialização mais rápida
- Fetch API nativo = melhor desempenho
- Retry integrado = maior confiabilidade
P: Posso usar v3 com JavaScript (não TypeScript)?
R: Com certeza! Os tipos TypeScript são opcionais. v3 funciona perfeitamente com JavaScript puro.
P: E quanto à compatibilidade com versões anteriores?
R: v3 não é compatível com v2. Por isso usamos controle de versão semântico. Siga este guia para migrar.
Precisa de Ajuda?
Boa migração! 🚀