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

relational-faker

v1.3.0

Published

Deterministic, topologically sorted relational mock data generator.

Readme

RelationalFaker


💡 Why RelationalFaker?

Standard mocking libraries (like faker.js) are excellent for generating scalar values but struggle with relational integrity.

  • The Problem: If you need to generate Users, Posts, and Comments, you have to manually manage the order. You can't generate a Comment before its Post.
  • The Solution: RelationalFaker analyzes your schema, builds a Dependency Graph, and uses Topological Sort to automatically determine the correct generation order.

✨ Features

  • 🧠 Auto-Dependency Resolution: Just define relations; the engine figures out the execution order.
  • 🤝 Many-to-Many Support: Generate unique, non-colliding pairs for join tables (v1.3).
  • 💾 Data Exporters: Export generated data to SQL (INSERT statements) or CSV formats (v1.2).
  • 🔄 Recursive / Self-Referencing Relations: Support for trees, nested comments, or organizational hierarchies (v1.1).
  • 🎯 Smart Constraints: Define rules like "End Date must be after Start Date" (v1.1).
  • 🛡️ TypeScript First: Fully typed definitions.
  • ⚛️ Deterministic Seeding: Reproducible test runs.

📦 Installation

npm install relational-faker @faker-js/faker
# or
yarn add relational-faker @faker-js/faker

🚀 Quick Start

import { RelationalFaker, f } from 'relational-faker';

const db = new RelationalFaker({
  users: {
    count: 5,
    schema: {
      id: f.uuid(),
      name: f.fullName(),
    }
  },

  tasks: {
    count: 20,
    schema: {
      id: f.uuid(),
      // Context-aware generation: Access the store to get current count
      title: f.custom((ctx) => `Task #${ctx.store.length + 1}`), 
      assigneeId: f.relation('users', 'id'),
      
      // ✨ Smart Constraint: Due date is always AFTER created date
      createdAt: f.date.past(),
      dueAt: f.date.soon(10, 'createdAt'), 
    }
  },
  
  categories: {
    count: 10,
    schema: {
      id: f.uuid(),
      name: f.custom(() => "Category"),
      // 🔄 Self-Reference: Categories can have parent categories
      parentId: f.relation('categories', 'id'), 
    }
  }
});

const data = db.generate();

console.log(data.users);
console.log(data.tasks); // Dates are logically consistent

🤝 Many-to-Many Relations (v1.3)

Standard random selection can cause duplicate primary keys in join tables (e.g., assigning the same Student to the same Course twice).

Use crossJoin to generate unique pairs automatically.

import { RelationalFaker, f, crossJoin } from 'relational-faker';

// 1. Create a cross-join generator for Students <-> Courses
const enrollments = crossJoin('students', 'courses');

const db = new RelationalFaker({
  students: { count: 3, schema: { id: f.uuid() } },
  courses: {  count: 3, schema: { id: f.uuid() } },
  
  // Join Table
  student_courses: {
    count: 5, // Must be <= (students.count * courses.count)
    schema: {
      // 2. Assign the unique pair generators
      studentId: enrollments.left,
      courseId: enrollments.right,
      enrolledAt: f.date.past()
    }
  }
});

💾 Data Export (v1.2)

You can export the generated data to SQL or CSV for seeding databases or external analysis.

import { RelationalFaker, Exporters } from 'relational-faker';

const db = new RelationalFaker({ /* config */ });
const data = db.generate();

// 1. SQL Export (PostgreSQL, MySQL, SQLite)
// Generates valid INSERT statements handling dates, nulls, and escaping.
const sql = Exporters.toSQL(data, { dialect: 'postgres' });
console.log(sql);
// Output: INSERT INTO "users" ("id", "name") VALUES ...

// 2. CSV Export
// Returns an object where keys are table names and values are CSV strings.
const csvFiles = Exporters.toCSV(data);
console.log(csvFiles['users']);
// Output: id,name,email...

📚 API Reference

f.relation(tableName, fieldName)

Creates a foreign key reference. Supports self-referencing (recursive) tables automatically. The engine guarantees that the referenced record exists.

crossJoin(tableA, tableB, fieldA?, fieldB?)

Creates a synchronized pair of generators that yield unique combinations (Cartesian Product) of the two tables.

  • Returns { left, right } field descriptors.

f.date.soon(days, refField?)

Generates a date in the future relative to refField.

  • days: Range of days to generate within.
  • refField: Name of another field in the same row (e.g., 'createdAt').

f.custom((context) => T)

Provides access to the generation context for complex logic.

  • context.row: The current row being generated.
  • context.store: The array of rows generated so far for the current table.
  • context.db: The complete database of previously generated tables.

Exporters.toSQL(data, options?)

Converts the generated data object into a SQL string.

  • options.dialect: 'postgres' (default), 'mysql', or 'sqlite'.

Exporters.toCSV(data)

Converts the generated data object into a record of CSV strings ({ tableName: csvString }).


🧪 Testing with Seeds

For unit tests, consistency is key. Use .seed() to ensure the same data is generated every time.

const mocker = new RelationalFaker(config);
mocker.seed(12345); 
const result = mocker.generate();

⚠️ Known Limitations

  • Cross-Table Circular Dependencies: While self-references (A -> A) are supported, direct circular loops between two tables (A -> B -> A) are currently detected and blocked to prevent infinite loops.
  • Large Datasets: Performance is optimized for typical testing scenarios (up to ~10k records).

🤝 Contributing

Contributions are welcome!

  1. Fork the project
  2. Create your feature branch
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

📄 License

Distributed under the MIT License.