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

decorated-factory

v2.0.0

Published

A factory decorators for creating objects with faker data

Readme

Decorated Factory

A declarative, type‑safe factory for generating realistic data in tests, fixtures and seeders – no hand‑written mocks, no hidden globals.


Table of contents


Overview

Decorated Factory combines metadata decorators with a fluent builder that creates objects only when – and only as deep as – you request them. It works with plain objects or class instances, supports complex relationships, and ships with helpers such as sequential IDs and UUIDs.

Why you might like it:

  • Static paths - autocompletion for nested strings like 'photo.description'.
  • Lazy relations - nested objects are populated only after you call .with().
  • Bring‑your‑own faker - pass any @faker-js/faker instance & locale.
  • Works everywhere - unit tests, integration tests, database seeders.

Installation

npm i decorated-factory @faker-js/faker reflect-metadata -D

or

yarn add decorated-factory @faker-js/faker reflect-metadata --dev

Quick‑start

import 'reflect-metadata';
import { fakerPT_BR } from '@faker-js/faker';
import { Factory, FactoryValue } from 'decorated-factory';

const factory = new Factory(fakerPT_BR);

class User {
  @FactoryValue(faker => faker.number.int({ min: 1, max: 1000 }))
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;
}

const user = factory.one(User).make();

Core concepts

| Concept | Description | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @FactoryValue(fn, opts?) | Decorates a property with a supplier function that receives faker and returns a value. Pass { isArray: true } to mark the field as an array of primitives. | | @FactoryType(() => T \| [T], opts?) | Declares that a property is another entity (T) or an array of them ([T]). Supports ES primitives (String, Number, Boolean, Date) and built‑in helpers like AutoIncrement, UUID. | | Factory.one(Type) | Starts a builder for a single instance. | | Factory.many(Type) | Starts a builder for an array (default length = 1). | | .with(amount?, path) | Opt‑in to populate a relation or array. For arrays pass a count: .with(5, 'photos'). Works recursively with dot‑notation ('photos.upload'). | | .set(path, value) | Overrides generated data – must be called after any relevant .with(). Works on primitives, objects and arrays. | | .without(path) | Removes a property from the output (after .with() if nested). | | .make(size?) | Materialises the object(s) as class instances. For many() you can pass the final array size here. | | .plain(size?) | Like .make() but returns plain objects – handy for JSON payloads. |


Built‑in JavaScript types

Decorated Factory can generate sensible defaults for JavaScript primitives out of the box.

class Document {
  @FactoryType(() => String)
  title: string;

  @FactoryType(() => Number)
  version: number;

  @FactoryType(() => Boolean)
  isPublished: boolean;

  @FactoryType(() => Date)
  createdAt: Date;

  @FactoryType(() => [String])
  tags: string[];
}

const document = factory.one(Document).with(3, 'tags').make();

Built‑in helpers

| Helper | Produces | Example | | --------------- | --------------------------------------------------- | -------------------------------------- | | AutoIncrement | Sequential integers starting at 1 (per builder) | 1, 2, 3… | | UUID | RFC‑4122 v4 UUID strings | d7f3e429‑9d5b‑42f9‑b7de‑8ba0e50bc9f6 |

class Task {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryType(() => UUID)
  taskId: string;
}

const task = factory.one(Task).make();

Generating instances

Declaring fields

class User {
  @FactoryValue(faker => faker.number.int({ min: 1, max: 1000 }))
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;
}

const singleUser = factory.one(User).make();
const fiveUsers = factory.many(User).make(5);

Array fields of primitives

class User {
  @FactoryValue(faker => faker.number.int({ min: 1, max: 1000 }))
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryValue(faker => faker.lorem.word(), { isArray: true })
  tags: string[];
}

const userWithFiveTags = factory
  .one(User)
  .with(5, 'tags')
  .make();

Defining relationships

One‑to‑one

class Photo {
  @FactoryValue(faker => faker.image.url())
  url: string;
}

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => Photo)
  photo: Photo;
}

const user = factory.one(User).with('photo').make();

One‑to‑many

class Photo {
  @FactoryValue(faker => faker.image.url())
  url: string;
}

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => [Photo])
  photos: Photo[];
}

const gallery = factory.one(User).with(5, 'photos').make();

Nested graphs & circular refs

class Upload {
  @FactoryValue(faker => faker.image.url())
  url: string;
}

class Photo {
  @FactoryValue(faker => faker.lorem.sentence())
  description: string;

  @FactoryType(() => Upload)
  upload: Upload;
}

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => [Photo])
  photos: Photo[];
}

const complexUser = factory
  .one(User)
  .with(3, 'photos')
  .with('photos.upload')
  .make();
class Tag {
  @FactoryValue(faker => faker.word.noun())
  name: string;
}

class Photo {
  @FactoryValue(faker => faker.image.url())
  url: string;

  @FactoryType(() => [Tag])
  tags: Tag[];
}

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => [Photo])
  photos: Photo[];
}

const userWithTaggedPhotos = factory
  .one(User)
  .with(4, 'photos')
  .with(2, 'photos.tags')
  .make();

Explicit circular reference

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => Photo)
  photo: Photo;
}

class Photo {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.image.url())
  url: string;

  @FactoryType(() => User)
  user: User;
}

const circular = factory
  .one(User)
  .with('photo')
  .with('photo.user')
  .make();

Key binding (foreign keys)

class Photo {
  @FactoryValue(faker => faker.image.url())
  url: string;

  userId: number; // copied from parent User.id
}

class User {
  @FactoryType(() => AutoIncrement)
  id: number;

  @FactoryValue(faker => faker.person.fullName())
  name: string;

  @FactoryType(() => [Photo], { key: 'id', inverseKey: 'userId' })
  photos: Photo[];
}

const userWithPhotos = factory.one(User).with(5, 'photos').make();

Rules:

  1. key must exist on the parent.
  2. inverseKey must exist on the child.
  3. Works for one‑to‑one and one‑to‑many.

Arrays & amounts

|Call| Behaviour | |---|-----------------------------------------------------------------| |.with(0, 'tags')| Creates an empty array. | |.with(-1, 'tags')| Throws – amounts must be ≥ 0. | |.many(Type).make(n)| Generates n root objects. 0[]; negative numbers throw. |

const emptyPhotosUser = factory.one(User).with(0, 'photos').make();

Overriding values with set

const namedUser = factory.one(User)
  .set('name', 'John Doe')
  .make();

Nested & array overrides

const modifiedDescriptions = factory.one(User)
  .with(5, 'photos')
  .set('photos.description', 'Same description for all photos')
  .make();

Overriding a nested path without first requesting its parent relation throws an error to preventing silent mistakes.


Excluding fields with without

const anonymousUser = factory
  .one(User)
  .without('name')
  .make();

Excluding inside a relation

const userWithoutPhotoText = factory
  .one(User)
  .with('photo')
  .without('photo.description')
  .make();

Plain vs class instances

.make() returns class instances; .plain() returns plain objects – perfect for HTTP payloads:

const payload = factory
  .one(User)
  .with(2, 'photos')
  .plain();

Error handling

Decorated Factory fails fast with clear errors when you:

  • Supply a negative amount to .with() or .many().make().
  • Call set() for a nested path without its parent .with().
  • Try to generate an array of AutoIncrement values (unsupported by design).

API reference

Factory.one(Type)  // builder for one
Factory.many(Type) // builder for many
  .with(amount?, path)   // opt-in relations (any depth)
  .set(path, value)      // overrides (optional)
  .without(path)         // exclusions (optional)
  .make(size?) | .plain(size?)