A v5 é a primeira release de funcionalidades desde a v3 (a v4 foi apenas o bump para Node 22). Ela traz muitos recursos novos — todos aditivos — e um conjunto pequeno de correções de contrato que são tecnicamente breaking.
npm install nfe-io@^5Na prática, a maioria dos projetos não precisa mudar nada. As quebras abaixo são em superfícies que já estavam quebradas (métodos que só lançavam 404, retornos que vinham
undefined). Se você usa essas APIs, ajuste conforme a seguir.
Antes devolvia um envelope tipado como { addresses: Address[] } (mas a API real retorna
um único endereço, então result.addresses vinha undefined). Agora retorna o Address
direto.
// Antes (v4) — não funcionava como documentado
const result = await nfe.addresses.lookupByPostalCode('01310-100');
const street = result.addresses[0].street; // undefined
// Agora (v5)
const address = await nfe.addresses.lookupByPostalCode('01310-100');
const street = address.street; // 'Paulista'Os endpoints (/v2/addresses e /v2/addresses/{term}) respondem 404 no host real —
os métodos só lançavam NotFoundError. Use lookupByPostalCode(). O tipo
AddressSearchOptions foi removido e AddressLookupResponse agora descreve o envelope
real ({ address: Address }).
Busca de endereço por texto livre só volta se/quando o backend expuser um endpoint real — aí como adição não-breaking.
O cancelamento de NFS-e é assíncrono (HTTP 202 + Location). Antes, cancel()
retornava um stub de polling tipado como ServiceInvoiceData (então cancelled.id /
cancelled.flowStatus vinham undefined). Agora retorna CancelInvoiceResponse.
// Antes (v4)
const cancelled = await nfe.serviceInvoices.cancel(companyId, invoiceId);
console.log(cancelled.flowStatus); // undefined
// Agora (v5) — união discriminada (espelha create())
const result = await nfe.serviceInvoices.cancel(companyId, invoiceId);
if (result.status === 'async') {
console.log(result.response.invoiceId);
}
// ou bloqueie até concluir:
const invoice = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId);
console.log(invoice.flowStatus); // 'Cancelled'O pré-flight local valida apenas o formato (buffer, senha presente, magic bytes
PKCS#12). Não retorna mais subject/issuer/validade inventados — esses dados são
verificados no servidor durante o upload do certificado. Se você lia result.metadata,
ele agora é ausente no pré-flight.
- Emissão RTC (Reforma Tributária):
nfe.serviceInvoicesRtc,nfe.productInvoicesRtc - NFC-e:
nfe.consumerInvoices - Inscrições municipais:
nfe.municipalTaxes - Certificados por thumbprint:
nfe.certificates - Notificações:
nfe.notifications - Webhooks de conta +
fetchEventTypes() companies.exists(),serviceInvoices.retrieveByExternalId(),serviceInvoices.cancelAndWait(),stateTaxes.switchAuthorizer()Companyenriquecido com campos do spec (opcionais)
A v4.0.0 não contém mudanças de API — todo código escrito para a v3 funciona sem alterações.
O único requisito novo é Node.js >= 22 (Node 18 e 20 atingiram fim de vida):
node --version # deve ser >= 22
npm install nfe-io@^4Se você ainda precisa de Node 18/20, permaneça no nfe-io@^3.2.
Internamente, a v4 moderniza o toolchain de build (tsup → tsdown/Rolldown, vitest 4) e corrige alertas de segurança em dependências de desenvolvimento — nada disso afeta o código consumidor.
Este guia ajuda você a migrar do SDK NFE.io v2.x para v3.0.
- Visão Geral
- Mudanças Incompatíveis
- Migração Passo a Passo
- Mudanças na API
- Exemplos de Código
- Perguntas Frequentes
✨ 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
- Node.js >= 18.0.0 (anteriormente v12 na v2)
- Mudanças incompatíveis na API (veja abaixo)
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
# v2 e v3 usam o mesmo nome
npm install nfe-io// 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' });// 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
}
});// 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);
}// 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);
}
}// 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);| 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 |
# Instalar novo pacote (v2 fica instalada por enquanto)
npm install nfe-io@3.0.0
# Verificar versão do Node.js
node --version # Deve ser >= 18.0.0- 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' });- 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();+ 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);
}
}// 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);
}# 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// 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
});// 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()
// 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
// 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');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);
}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);
}
}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 };
}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);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' });R: Não! Sua API key existente funciona tanto com v2 quanto com v3.
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
R: Não! Os formatos de request/response da API são os mesmos. Apenas a interface do SDK mudou.
R: Mantenha-o até que você tenha migrado e testado completamente. Depois, atualize para a versão 3.x.x.
R: Sim! v3 é mais rápido:
- Sem dependências externas = inicialização mais rápida
- Fetch API nativo = melhor desempenho
- Retry integrado = maior confiabilidade
R: Com certeza! Os tipos TypeScript são opcionais. v3 funciona perfeitamente com JavaScript puro.
R: v3 não é compatível com v2. Por isso usamos controle de versão semântico. Siga este guia para migrar.
Boa migração! 🚀