npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

json-idempofy

v1.0.0

Published

Creates a unique fingerprint from JSON payloads for reliable idempotency in microservices

Readme

json-idempofy

Create reliable, unique fingerprints from JSON data to prevent duplicate records in your microservices.

npm version License: ISC TypeScript

🎯 The Problem We Solve

Ever had duplicate records when switching API providers?

Imagine you're integrating with Open Finance providers (like Belvo, Pluggy, or Yapily). You start with Provider A, then switch to Provider B. Provider B sends the same historical transactions with different IDs, creating duplicates in your database.

json-idempofy solves this by creating unique fingerprints based on business data, not provider-specific IDs.

✨ Why Choose json-idempofy?

  • 🛡️ Prevents Duplicates: Never store the same transaction twice, even from different providers
  • 🔄 Partner Transitions: Seamlessly switch between API providers without data loss
  • ⚡ High Performance: Optimized for microservices with minimal overhead
  • 🔒 Enterprise Security: HMAC-SHA256 support for sensitive financial data
  • 📊 Standards Compliant: Uses JCS (JSON Canonicalization Scheme) for maximum reliability
  • 🚀 Zero Dependencies: Works with any Node.js project

🚀 Quick Start

npm install json-idempofy
# or
yarn add json-idempofy
# or
pnpm add json-idempofy
import { Idempofy } from 'json-idempofy';

// Create a unique fingerprint for any JSON data
const transaction = {
  amount: 150.75,
  currency: 'USD',
  description: 'Coffee Shop Purchase',
  merchant: { name: 'Starbucks', category: 'Food' },
  date: '2023-06-15T14:30:00Z',
};

const fingerprint = Idempofy.strict(transaction);
console.log(fingerprint); // "a1b2c3d4e5f6..." - unique identifier

🏦 Real-World Example: Open Finance Integration

Scenario: You're switching from Provider A to Provider B for Open Finance data. Both providers send the same transactions with different IDs.

// Transaction from Provider A
const providerATransaction = {
  id: 'provider_a_txn_12345', // Provider-specific ID
  amount: 150.75,
  currency: 'USD',
  description: 'Coffee Shop',
  merchant: { name: 'Starbucks' },
  date: '2023-06-15T14:30:00Z',
};

// Same transaction from Provider B (different ID)
const providerBTransaction = {
  id: 'provider_b_txn_98765', // Different provider ID
  amount: 150.75, // Same business data
  currency: 'USD',
  description: 'Coffee Shop',
  merchant: { name: 'Starbucks' },
  date: '2023-06-15T14:30:00Z',
};

// Create fingerprints based on business data only
const businessFields = ['amount', 'currency', 'description', 'merchant.name', 'date'];

const providerAFingerprint = Idempofy.selective(providerATransaction, businessFields);
const providerBFingerprint = Idempofy.selective(providerBTransaction, businessFields);

console.log(providerAFingerprint === providerBFingerprint); // true - same transaction!

🎯 Use Cases

💳 Financial Services

  • Open Finance: Prevent duplicate transactions when switching providers
  • Payment Processing: Ensure idempotent payment requests
  • Banking APIs: Handle account data from multiple sources

🔄 Data Integration

  • API Migrations: Switch providers without data duplication
  • Data Synchronization: Keep systems in sync across environments
  • ETL Pipelines: Prevent duplicate records in data warehouses

🏢 Enterprise Applications

  • Microservices: Ensure idempotent operations across services
  • Event Processing: Deduplicate events in event-driven architectures
  • Audit Trails: Create consistent audit identifiers

🎯 Fingerprinting Strategies

Choose the right strategy for your use case:

1. Strict Strategy - Full Payload Fingerprinting

const fingerprint = Idempofy.strict(payload);
// or
const fingerprint = Idempofy.this(payload, { strategy: 'strict' });

When to use:

  • ✅ Complete data integrity checking
  • ✅ API request/response validation
  • ✅ Document versioning
  • ✅ When you need to detect ANY change

Example:

const apiRequest = {
  userId: 123,
  amount: 100,
  currency: 'USD',
  timestamp: '2023-06-15T10:30:00Z',
  metadata: { source: 'mobile' },
};

const fingerprint = Idempofy.strict(apiRequest);
// Any change in ANY field will create a different fingerprint

2. Selective Strategy - Choose Specific Fields

const fingerprint = Idempofy.selective(payload, ['userId', 'amount', 'currency']);
// or
const fingerprint = Idempofy.this(payload, {
  strategy: 'selective',
  fields: ['userId', 'amount', 'currency'],
});

When to use:

  • ✅ Open Finance transactions (ignore provider-specific IDs)
  • ✅ Payment processing (ignore timestamps)
  • ✅ User data (ignore metadata)
  • ✅ When you want to ignore certain fields

Example:

const transaction = {
  id: 'txn_12345', // Provider-specific ID
  amount: 100,
  currency: 'USD',
  description: 'Coffee',
  timestamp: '2023-06-15T10:30:00Z', // Processing time
  metadata: { source: 'api' }, // System metadata
};

// Only fingerprint business data, ignore IDs and timestamps
const fingerprint = Idempofy.selective(transaction, ['amount', 'currency', 'description']);
// Changes to id, timestamp, or metadata won't affect the fingerprint

3. Semantic Strategy - Smart Normalization

const fingerprint = Idempofy.semantic(payload);
// or
const fingerprint = Idempofy.this(payload, { strategy: 'semantic' });

When to use:

  • ✅ Business logic fingerprinting
  • ✅ Data with date variations
  • ✅ Number precision issues
  • ✅ When you need intelligent normalization

Example:

const payload1 = { date: new Date('2023-06-15T10:30:00Z') };
const payload2 = { date: '2023-06-15T10:30:00.000Z' };

// Semantic strategy normalizes dates automatically
const fingerprint1 = Idempofy.semantic(payload1);
const fingerprint2 = Idempofy.semantic(payload2);
console.log(fingerprint1 === fingerprint2); // true - same date!

4. Custom Strategy - Full Control with Transformations

const fingerprint = Idempofy.custom(payload, ['email', 'amount'], {
  email: { $lower: 'email' },
  amount: { $round: 2 },
});
// or
const fingerprint = Idempofy.this(payload, {
  strategy: 'custom',
  fields: ['email', 'amount'],
  transformations: {
    email: { $lower: 'email' },
    amount: { $round: 2 },
  },
});

When to use:

  • ✅ Data normalization needed
  • ✅ Complex field transformations
  • ✅ Conditional logic required
  • ✅ When you need maximum control

Example:

const userData = {
  email: '[email protected]',
  name: '  John Doe  ',
  amount: 99.999,
  status: 'active',
};

const fingerprint = Idempofy.custom(userData, ['email', 'name', 'amount'], {
  email: { $lower: 'email' }, // Convert to lowercase
  name: { $trim: 'name' }, // Remove whitespace
  amount: { $round: 2 }, // Round to 2 decimal places
});
// Result: email='[email protected]', name='John Doe', amount=100.00

Strategy Comparison

| Strategy | Use Case | Fields | Normalization | Best For | | ------------- | ------------------ | ------------- | ---------------------- | ------------------------------------- | | Strict | Complete integrity | All fields | None | API validation, document versioning | | Selective | Business logic | Chosen fields | None | Open Finance, payment processing | | Semantic | Smart matching | All fields | Automatic | Business data with variations | | Custom | Complex needs | Chosen fields | Custom transformations | Data normalization, conditional logic |

Quick Decision Guide

"I need to detect ANY change" → Use Strict
"I only care about specific fields" → Use Selective
"I have data variations (dates, numbers)" → Use Semantic
"I need custom transformations" → Use Custom

📖 API Reference

Basic Usage

import { Idempofy } from 'json-idempofy';

// Simple fingerprinting
const fingerprint = Idempofy.strict(payload);

// Selective fingerprinting (ignore certain fields)
const fingerprint = Idempofy.selective(payload, ['userId', 'amount', 'currency']);

// Smart fingerprinting (handles dates, numbers automatically)
const fingerprint = Idempofy.semantic(payload);

Advanced Usage

// Custom transformations
const fingerprint = Idempofy.custom(payload, ['email', 'amount'], {
  email: { $lower: 'email' }, // Convert to lowercase
  amount: { $round: 2 }, // Round to 2 decimal places
});

// High-security fingerprinting
const fingerprint = Idempofy.detailed(payload, {
  strategy: 'selective',
  fields: ['userId', 'amount'],
  hashing: {
    algorithm: 'hmac-sha256',
    secretKey: 'your-secret-key',
    keyId: 'key-2024',
  },
});

Nested Field Access

// Access deeply nested fields
const payload = {
  user: { id: 123, name: 'John' },
  items: [{ product: { id: 'P1', name: 'Laptop' }, quantity: 1 }],
};

const fingerprint = Idempofy.selective(payload, [
  'user.id',
  'user.name',
  'items[0].product.id',
  'items[0].product.name',
]);

🔧 Configuration Options

Hashing Algorithms

// SHA-256 (default) - Fast and reliable
{ algorithm: 'sha256' }

// HMAC-SHA256 - High security with secret key
{
  algorithm: 'hmac-sha256',
  secretKey: 'your-secret-key',
  keyId: 'key-2024'
}

// HMAC-SHA512 - Maximum security
{
  algorithm: 'hmac-sha512',
  secretKey: 'your-secret-key',
  keyId: 'key-2024'
}

Field Transformations

const transformations = {
  // String transformations
  email: { $lower: 'email' }, // Convert to lowercase
  name: { $trim: 'name' }, // Remove whitespace

  // Number transformations
  amount: { $round: 2 }, // Round to 2 decimal places

  // Date transformations
  date: { $date: 'timestamp' }, // Convert to ISO date string

  // Conditional logic
  discount: {
    $if: {
      condition: 'type',
      then: 'discount',
      else: '0',
    },
  },
};

🏗️ Production Implementation

Database Deduplication

class TransactionProcessor {
  async processTransaction(transaction) {
    // Create idempotency fingerprint
    const fingerprint = Idempofy.selective(transaction, [
      'amount',
      'currency',
      'description',
      'merchant.name',
      'date',
    ]);

    // Check for existing transaction
    const existing = await this.findByFingerprint(fingerprint);

    if (existing) {
      console.log('Duplicate transaction detected');
      return { action: 'skip', existingId: existing.id };
    }

    // Store new transaction
    await this.storeTransaction(transaction, fingerprint);
    return { action: 'stored', fingerprint };
  }
}

Microservice Integration

// Express.js middleware
app.post('/api/transactions', async (req, res) => {
  const fingerprint = Idempofy.selective(req.body, businessFields);

  const existing = await db.transactions.findOne({ fingerprint });
  if (existing) {
    return res.json({ message: 'Transaction already processed', id: existing.id });
  }

  const transaction = await db.transactions.create({
    ...req.body,
    fingerprint,
  });

  res.json({ id: transaction.id, fingerprint });
});

🔒 Security & Compliance

  • PCI DSS: HMAC-SHA256 for payment data
  • SOX: Audit trail support with consistent fingerprints
  • GDPR: Data minimization with selective field fingerprinting
  • ISO 27001: Enterprise-grade security features

📊 Performance

  • Speed: ~160ms for 1000 HMAC-SHA256 operations
  • Memory: Minimal memory footprint
  • Scalability: Handles millions of transactions
  • Reliability: 99.99% uptime with proper error handling

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📝 License

This project is licensed under the ISC License - see the LICENSE file for details.

🙏 Acknowledgments

  • Inspired by MongoDB's aggregation pipeline operators
  • Built with TypeScript for type safety and developer experience
  • Designed for modern JavaScript/TypeScript applications
  • Part of the json-* family: json-idempofy, json-parsefy

Made with ❤️ by Daniel Marinho

Solving real-world integration challenges with reliable, enterprise-grade solutions.