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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@doviui/dev-db

v0.4.0

Published

TypeScript-first mock database generator for rapid application development

Downloads

507

Readme

dev-db

TypeScript-first mock database generator for rapid application development

dev-db eliminates the friction of setting up databases during development. Define your data model with a type-safe schema, generate realistic mock data instantly, and iterate faster without database infrastructure overhead.

Why dev-db?

For Frontend Developers: Build and test UI components with realistic data without waiting for backend APIs. Generate hundreds of records in seconds and work independently.

For Backend Developers: Prototype schemas, test business logic, and validate data models before committing to a database. Catch schema errors early with built-in validation.

For Teams: Share reproducible datasets across development, testing, and CI/CD environments using seeds. Onboard new developers instantly with pre-generated data.

Key Features

  • Type-Safe Schema Definition - Fluent TypeScript API with full IntelliSense support
  • TypeScript Types Generation - Auto-generate type definitions from schemas for type-safe data consumption
  • Automatic Relationship Resolution - Foreign keys handled intelligently with topological sorting
  • Production-Quality Mock Data - Powered by Faker.js for realistic, diverse datasets
  • Built-In Validation - Detect circular dependencies, missing tables, and constraint conflicts before generation
  • Reproducible Datasets - Seed-based generation for consistent data across environments
  • Zero Configuration - Works out of the box, no database setup required

Installation

bun add @doviui/dev-db
# or
npm install @doviui/dev-db
# or
yarn add @doviui/dev-db
# or
pnpm add @doviui/dev-db

Quick Start

Step 1: Define Your Schema

Create schema files using the fluent TypeScript API:

// schemas/user.schema.ts
import { t } from '@doviui/dev-db'

export default {
  User: {
    $count: 100, // Generate 100 users
    
    id: t.bigserial().primaryKey(),
    username: t.varchar(50).unique().notNull().generate('internet.userName'),
    email: t.varchar(255).unique().notNull().generate('internet.email'),
    full_name: t.text().generate('person.fullName'),
    age: t.integer().min(18).max(90),
    is_active: t.boolean().default(true),
    created_at: t.timestamptz().default('now')
  }
}
// schemas/post.schema.ts
import { t } from '@doviui/dev-db'

export default {
  Post: {
    $count: 500, // Generate 500 posts
    
    id: t.bigserial().primaryKey(),
    user_id: t.foreignKey('User', 'id').notNull(),
    title: t.varchar(200).generate('lorem.sentence'),
    content: t.text().generate('lorem.paragraphs'),
    status: t.varchar(20).enum(['draft', 'published', 'archived']).default('draft'),
    view_count: t.integer().default(0),
    created_at: t.timestamptz().default('now')
  }
}

Step 2: Generate Your Data

Using the CLI directly

Run the CLI to generate JSON files from a single schema file or a directory:

# Generate from a single schema file
bunx @doviui/dev-db schema.ts

# Generate from a directory of schema files
bunx @doviui/dev-db ./schemas

# Specify output directory
bunx @doviui/dev-db schema.ts -o ./data

# Use a seed for reproducible data
bunx @doviui/dev-db ./schemas --seed 42

# Combine options
bunx @doviui/dev-db ./schemas -o ./mock-data -s 42

Directory Support: When a directory is provided, all .ts and .js files are loaded and their schemas are merged. This allows you to organize related tables across multiple files for better maintainability.

Adding to package.json scripts

Add generation to your project's scripts:

{
  "scripts": {
    "generate:data": "bunx @doviui/dev-db ./schemas -o ./mock-data",
    "generate:data:seed": "bunx @doviui/dev-db ./schemas -o ./mock-data -s 42"
  }
}

Then run with:

bun run generate:data
# or with a seed
bun run generate:data:seed

Step 3: Consume Your Data

Import the generated JSON and use it in your application:

// server.ts
import users from './mock-data/User.json'
import posts from './mock-data/Post.json'

// Simple query helpers
function getUserById(id: number) {
  return users.find(u => u.id === id)
}

function getPostsByUserId(userId: number) {
  return posts.filter(p => p.user_id === userId)
}

// Use in your API
app.get('/users/:id', (req, res) => {
  const user = getUserById(parseInt(req.params.id))
  if (!user) return res.status(404).json({ error: 'User not found' })

  const userPosts = getPostsByUserId(user.id)
  res.json({ ...user, posts: userPosts })
})

Type-Safe Data Consumption

Generate TypeScript type definitions from your schemas for full type safety when consuming the mock data:

# Generate types alongside JSON data
bunx @doviui/dev-db schema.ts --types

This creates a types.ts file with interfaces matching your schema:

// mock-data/types.ts (auto-generated)
export interface User {
  id: number;
  username: string;
  email: string;
  age: number | null;
  created_at?: string;
}

export interface Post {
  id: number;
  user_id: number;  // Correctly typed based on User.id
  title: string;
  content: string | null;
  created_at?: string;
}

Import and use the types in your application:

// server.ts
import users from './mock-data/User.json'
import posts from './mock-data/Post.json'
import type { User, Post } from './mock-data/types'

// Fully type-safe!
function getUserById(id: number): User | undefined {
  return users.find(u => u.id === id)
}

function getPostsByUserId(userId: number): Post[] {
  return posts.filter(p => p.user_id === userId)
}

Programmatic API:

import { TypesGenerator } from '@doviui/dev-db'

const typesGenerator = new TypesGenerator(schema, {
  outputDir: './mock-data',
  fileName: 'types.ts'  // Optional, defaults to 'types.ts'
})

await typesGenerator.generate()

API Reference

Data Types

dev-db supports a comprehensive set of SQL-inspired data types:

Numeric Types

t.bigint()       // Big integer
t.bigserial()    // Auto-incrementing big integer
t.integer()      // Standard integer
t.smallint()     // Small integer
t.serial()       // Auto-incrementing integer
t.decimal(10, 2) // Decimal with precision and scale
t.numeric(10, 2) // Alias for decimal
t.real()         // Floating point
t.double()       // Double precision float

String Types

t.varchar(255)   // Variable-length string
t.char(10)       // Fixed-length string
t.text()         // Unlimited text

Date/Time Types

t.date()         // Date only
t.time()         // Time only
t.timestamp()    // Date and time
t.timestamptz()  // Date and time with timezone

Other Types

t.boolean()      // True/false
t.uuid()         // UUID v4

// JSON types with optional structured schemas
t.json()         // JSON object (unstructured)
t.jsonb()        // JSON binary (unstructured)

// Structured JSON with type-safe schema
t.json({
  id: t.integer(),
  name: t.varchar(100),
  preferences: t.json({
    theme: t.varchar(20),
    notifications: t.boolean()
  })
})

Relationships

t.foreignKey('TableName', 'column')           // Foreign key reference
t.belongsTo('TableName', 'foreignKeyField')   // Many-to-one relationship
t.hasMany('TableName', 'foreignKeyField', { min: 1, max: 5 })  // One-to-many relationship (virtual)

Field Modifiers

Chain modifiers to configure field behavior and constraints:

// Constraints
.primaryKey()           // Mark as primary key
.unique()              // Enforce uniqueness
.notNull()             // Cannot be null
.nullable()            // Can be null (default)

// Defaults
.default(value)        // Set default value
.default('now')        // Special: current timestamp

// Ranges (for numeric types)
.min(18)               // Minimum value
.max(90)               // Maximum value

// Enums
.enum(['draft', 'published', 'archived'])

// Custom generation
.generate('internet.email')           // Use Faker.js method
.generate(() => Math.random() * 100)  // Custom function

Advanced Usage

hasMany and belongsTo Relationships

For clearer relationship semantics and automatic record count calculation, use hasMany() and belongsTo() helpers:

import { t } from '@doviui/dev-db'

export default {
  User: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    username: t.varchar(50).unique().generate('internet.userName'),

    // Define one-to-many: each user has 2-5 posts
    posts: t.hasMany('Post', 'userId', { min: 2, max: 5 })
  },

  Post: {
    // $count is automatically calculated: 100 users * avg(2,5) posts = 350 posts
    id: t.bigserial().primaryKey(),
    userId: t.integer(),  // The actual foreign key field

    // Define many-to-one for clearer intent
    author: t.belongsTo('User', 'userId'),

    title: t.varchar(200).generate('lorem.sentence'),
    content: t.text().generate('lorem.paragraphs')
  }
}

Key Benefits:

  • Auto-calculated counts: hasMany automatically calculates child table record counts based on parent count and min/max constraints
  • Clearer intent: belongsTo makes many-to-one relationships more explicit than raw foreignKey
  • Virtual fields: Both hasMany and belongsTo fields don't appear in generated output—they're metadata for generation logic

How it works:

  1. hasMany('Post', 'userId', { min: 2, max: 5 }) tells the generator: "Each User should have between 2-5 Posts"
  2. The generator calculates: 100 users * average(2, 5) = 100 * 3.5 = 350 posts
  3. belongsTo('User', 'userId') instructs the generator to populate the userId field with valid User IDs
  4. Neither posts nor author appear in the generated JSON—only the actual data fields

Complex Example:

export default {
  Author: {
    $count: 50,
    id: t.uuid().primaryKey(),
    name: t.varchar(100).generate('person.fullName'),
    articles: t.hasMany('Article', 'authorId', { min: 3, max: 10 })
  },

  Article: {
    // Auto-calculated: 50 * 6.5 = 325 articles
    id: t.bigserial().primaryKey(),
    authorId: t.varchar(36),  // UUID foreign key
    author: t.belongsTo('Author', 'authorId'),
    title: t.varchar(200),

    comments: t.hasMany('Comment', 'articleId', { min: 0, max: 20 })
  },

  Comment: {
    // Auto-calculated: 325 * 10 = 3,250 comments
    id: t.bigserial().primaryKey(),
    articleId: t.integer(),
    article: t.belongsTo('Article', 'articleId'),
    content: t.text()
  }
}

Multi-Table Schemas

You can organize schemas in two ways:

Option 1: Single file with multiple tables

Define multiple related tables in a single file:

// schemas/social.schema.ts
import { t } from '@doviui/dev-db'

export default {
  User: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    username: t.varchar(50).unique()
  },

  Post: {
    $count: 500,
    id: t.bigserial().primaryKey(),
    user_id: t.foreignKey('User', 'id'),
    title: t.varchar(200)
  },

  Comment: {
    $count: 2000,
    id: t.uuid().primaryKey(),
    post_id: t.foreignKey('Post', 'id'),
    user_id: t.foreignKey('User', 'id'),
    content: t.text()
  }
}

Option 2: Multiple files in a directory

Organize each table in its own file for better maintainability:

// schemas/users.ts
import { t } from '@doviui/dev-db'

export default {
  User: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    username: t.varchar(50).unique()
  }
}
// schemas/posts.ts
import { t } from '@doviui/dev-db'

export default {
  Post: {
    $count: 500,
    id: t.bigserial().primaryKey(),
    user_id: t.foreignKey('User', 'id'),
    title: t.varchar(200)
  }
}
// schemas/comments.ts
import { t } from '@doviui/dev-db'

export default {
  Comment: {
    $count: 2000,
    id: t.uuid().primaryKey(),
    post_id: t.foreignKey('Post', 'id'),
    user_id: t.foreignKey('User', 'id'),
    content: t.text()
  }
}

Then generate with: bunx @doviui/dev-db ./schemas

Schema Validation

dev-db validates schemas before generation to catch errors early:

// ❌ This will fail validation
export default {
  Post: {
    id: t.bigserial().primaryKey(),
    user_id: t.foreignKey('User', 'id')  // Error: User table doesn't exist!
  }
}

Output:

Schema validation failed:

  Post.user_id: Foreign key references non-existent table 'User'

Structured JSON Fields

Define type-safe JSON schemas for complex nested data structures. This generates proper TypeScript types instead of any, and creates realistic structured data:

import { t } from '@doviui/dev-db'

export default {
  User: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    email: t.varchar(255).unique().notNull(),

    // Structured JSON field with nested schema
    profile: t.json({
      firstName: t.varchar(50).generate('person.firstName'),
      lastName: t.varchar(50).generate('person.lastName'),
      age: t.integer().min(18).max(90),

      // Deeply nested structures
      preferences: t.json({
        theme: t.varchar(20).enum(['light', 'dark', 'auto']).default('auto'),
        notifications: t.json({
          email: t.boolean().default(true),
          push: t.boolean().default(false),
          frequency: t.varchar(20).enum(['realtime', 'daily', 'weekly'])
        })
      })
    }),

    // Simple unstructured JSON (fallback)
    metadata: t.jsonb()
  }
}

Generated TypeScript types:

// mock-data/types.ts
export interface User {
  id: number;
  email: string;
  profile: {
    firstName: string;
    lastName: string;
    age: number;
    preferences: {
      theme?: string;  // Optional because it has a default
      notifications: {
        email?: boolean;
        push?: boolean;
        frequency: string;
      };
    };
  };
  metadata: any;  // Unstructured JSON falls back to 'any'
}

Generated JSON data:

{
  "id": 1,
  "email": "[email protected]",
  "profile": {
    "firstName": "John",
    "lastName": "Doe",
    "age": 34,
    "preferences": {
      "theme": "dark",
      "notifications": {
        "email": true,
        "push": false,
        "frequency": "daily"
      }
    }
  },
  "metadata": { "data": "sample" }
}

Benefits:

  • ✅ Full TypeScript type safety for nested JSON structures
  • ✅ All field modifiers work (.nullable(), .default(), .enum(), .generate(), etc.)
  • ✅ Supports unlimited nesting depth
  • ✅ Works with both t.json() and t.jsonb()

Custom Data Generators

Leverage any Faker.js method for realistic data generation:

t.varchar(100).generate('company.name')
t.varchar(50).generate('location.city')
t.integer().generate('number.int', { min: 1000, max: 9999 })

Or write custom functions:

t.varchar(20).generate(() => {
  const colors = ['red', 'blue', 'green', 'yellow']
  return colors[Math.floor(Math.random() * colors.length)]
})

t.jsonb().generate((faker) => ({
  preferences: {
    theme: faker.helpers.arrayElement(['light', 'dark']),
    language: faker.helpers.arrayElement(['en', 'es', 'fr'])
  },
  lastLogin: faker.date.recent().toISOString()
}))

Command Line Interface

dev-db <path> [options]

Arguments:
  <path>                 Path to schema file or directory containing schema files

Options:
  -o, --output <dir>     Output directory for generated JSON files (default: ./mock-data)
  -s, --seed <number>    Random seed for reproducible data generation
  -t, --types            Generate TypeScript type definitions alongside JSON
  -h, --help             Show help message

Examples:

# Single file
bunx @doviui/dev-db schema.ts

# Directory of schemas
bunx @doviui/dev-db ./schemas

# Custom output directory
bunx @doviui/dev-db ./schemas -o ./data

# Reproducible generation with seed
bunx @doviui/dev-db schema.ts -s 42

# Generate TypeScript types
bunx @doviui/dev-db schema.ts --types

# All options combined
bunx @doviui/dev-db ./schemas --output ./database --seed 12345 --types

Schema File Format:

Schema files must export a default object or named schema export:

import { t } from '@doviui/dev-db';

export default {
  User: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    email: t.varchar(255).unique().notNull().generate('internet.email'),
    name: t.varchar(100).notNull()
  }
};

Directory Support:

When a directory is provided, all .ts and .js files are loaded and their schemas are merged. This allows you to organize schemas across multiple files:

schemas/
  ├── users.ts      // exports { User: { ... } }
  ├── posts.ts      // exports { Post: { ... } }
  └── comments.ts   // exports { Comment: { ... } }

Running bunx @doviui/dev-db ./schemas will merge all three schemas and generate data for all tables.

Programmatic API

For more control, create a custom generation script:

// scripts/generate-data.ts
import { MockDataGenerator, SchemaValidator } from '@doviui/dev-db'
import { schema } from './schemas/index'

// Validate schema
const validator = new SchemaValidator()
const errors = validator.validate(schema)

if (errors.length > 0) {
  console.error('Schema validation failed:')
  errors.forEach(err => {
    const location = err.field ? `${err.table}.${err.field}` : err.table
    console.error(`  ${location}: ${err.message}`)
  })
  process.exit(1)
}

// Generate data
const generator = new MockDataGenerator(schema, {
  outputDir: './mock-data',
  seed: process.env.SEED ? parseInt(process.env.SEED) : undefined
})

await generator.generate()
console.log('Mock data generated successfully!')

Add to package.json:

{
  "scripts": {
    "generate:data": "bun run scripts/generate-data.ts"
  }
}

Then run with:

bun run generate:data
# or with a custom seed
SEED=42 bun run generate:data

Real-World Examples

E-Commerce Platform

// schemas/ecommerce.schema.ts
import { t } from '@doviui/dev-db'

export default {
  Customer: {
    $count: 200,
    id: t.uuid().primaryKey(),
    email: t.varchar(255).unique().generate('internet.email'),
    first_name: t.varchar(50).generate('person.firstName'),
    last_name: t.varchar(50).generate('person.lastName'),
    phone: t.varchar(20).generate('phone.number'),
    created_at: t.timestamptz().default('now'),

    // Each customer has 1-5 orders
    orders: t.hasMany('Order', 'customerId', { min: 1, max: 5 })
  },

  Product: {
    $count: 100,
    id: t.bigserial().primaryKey(),
    name: t.varchar(200).generate('commerce.productName'),
    description: t.text().generate('commerce.productDescription'),
    price: t.decimal(10, 2).min(5).max(5000),
    stock: t.integer().min(0).max(1000),
    category: t.varchar(50).enum(['electronics', 'clothing', 'home', 'books'])
  },

  Order: {
    // Auto-calculated: 200 * 3 = 600 orders
    id: t.bigserial().primaryKey(),
    customerId: t.varchar(36),
    customer: t.belongsTo('Customer', 'customerId'),
    status: t.varchar(20).enum(['pending', 'processing', 'shipped', 'delivered']),
    total: t.decimal(10, 2).min(10).max(10000),
    created_at: t.timestamptz().default('now'),

    // Each order has 1-4 items
    items: t.hasMany('OrderItem', 'orderId', { min: 1, max: 4 })
  },

  OrderItem: {
    // Auto-calculated: 600 * 2.5 = 1,500 items
    id: t.bigserial().primaryKey(),
    orderId: t.integer(),
    order: t.belongsTo('Order', 'orderId'),
    productId: t.integer(),
    product: t.belongsTo('Product', 'productId'),
    quantity: t.integer().min(1).max(10),
    price: t.decimal(10, 2)
  }
}

Blog Platform

// schemas/blog.schema.ts
import { t } from '@doviui/dev-db'

export default {
  Author: {
    $count: 50,
    id: t.uuid().primaryKey(),
    username: t.varchar(50).unique().generate('internet.userName'),
    email: t.varchar(255).unique().generate('internet.email'),
    bio: t.text().generate('lorem.paragraph'),
    avatar_url: t.varchar(500).generate('image.avatar'),

    // Each author has 3-10 articles
    articles: t.hasMany('Article', 'authorId', { min: 3, max: 10 })
  },

  Article: {
    // Auto-calculated: 50 * 6.5 = 325 articles
    id: t.bigserial().primaryKey(),
    authorId: t.varchar(36),
    author: t.belongsTo('Author', 'authorId'),
    title: t.varchar(200).generate('lorem.sentence'),
    slug: t.varchar(200).unique().generate('lorem.slug'),
    content: t.text().generate('lorem.paragraphs', 5),
    excerpt: t.varchar(500).generate('lorem.paragraph'),
    published: t.boolean(),
    published_at: t.timestamptz().nullable(),
    created_at: t.timestamptz().default('now')
  },

  Tag: {
    $count: 30,
    id: t.serial().primaryKey(),
    name: t.varchar(50).unique().generate('lorem.word')
  },

  ArticleTag: {
    $count: 800,
    articleId: t.integer(),
    article: t.belongsTo('Article', 'articleId'),
    tagId: t.integer(),
    tag: t.belongsTo('Tag', 'tagId')
  }
}

Best Practices

Schema Design

Define independent tables first - Structure your schemas so tables without foreign keys are defined before those with dependencies. This simplifies validation and generation.

Use realistic record counts - Set $count values that reflect production ratios. For example, if users typically have 5 posts, generate 100 users and 500 posts.

Leverage domain-specific generators - Use Faker.js methods that match your domain (e.g., company.name for business data, person.firstName for user data) to generate realistic datasets.

Development Workflow

Validate frequently - Run generation regularly during schema development to catch errors early. The validator provides immediate feedback on structural issues.

Use seeds for reproducibility - In test environments and CI/CD, use the --seed option to generate identical datasets across runs. This ensures consistent test results.

Organize by domain - Group related tables in single schema files (e.g., auth.schema.ts, orders.schema.ts) for better maintainability and clearer relationships.

Troubleshooting

Foreign Key Validation Errors

Problem: Foreign key references non-existent table 'TableName'

Solution: Ensure all referenced tables are defined in your schema before tables that reference them:

// ✅ Correct - User defined before Post
export default {
  User: {
    id: t.bigserial().primaryKey(),
    // ... other fields
  },
  Post: {
    id: t.bigserial().primaryKey(),
    user_id: t.foreignKey('User', 'id')  // User exists in schema
  }
}

Unique Constraint Errors

Problem: Could not generate unique value after 1000 attempts

Solution: This occurs when unique constraints are too restrictive for the number of records. Consider these options:

  • Increase variety: Use a more diverse Faker.js generator that produces more unique values
  • Reduce record count: Lower the $count if you don't need that many records
  • Remove constraint: If uniqueness isn't critical for your use case, remove the .unique() modifier

Circular Dependency Errors

Problem: Circular dependency detected involving table: TableName

Solution: dev-db cannot resolve circular foreign key relationships. Restructure your schema using one of these approaches:

  • Make relationships optional: Use .nullable() on one of the foreign keys
  • Introduce a junction table: Break the circular dependency with an intermediary table
  • Reconsider the model: Circular dependencies often indicate a modeling issue

Contributing

We welcome contributions! Whether you're fixing bugs, improving documentation, or proposing new features, your input helps make dev-db better for everyone.

Please read our Contributing Guide to get started.

License

MIT License - see LICENSE for details

Acknowledgments

dev-db is built with:

  • Faker.js - High-quality fake data generation
  • Bun - Fast JavaScript runtime and toolkit