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

@itrocks/lazy-loading

v0.2.1

Published

Integrates lazy loading for objects and collections in TypeScript classes

Downloads

248

Readme

npm version npm downloads GitHub issues discord

lazy-loading

Integrates lazy loading for objects and collections in TypeScript classes.

This documentation was written by an artificial intelligence and may contain errors or approximations. It has not yet been fully reviewed by a human. If anything seems unclear or incomplete, please feel free to contact the author of this package.

Installation

npm i @itrocks/lazy-loading

You will typically use this package together with @itrocks/store and @itrocks/storage, which provide the actual persistence layer.

Usage

@itrocks/lazy-loading hooks into Node's module loader to automatically wrap your entity classes and add lazy properties to them.

At application startup, call initLazyLoading() before importing the modules that declare your entity classes. Any exported entity class with @itrocks/store metadata will then be replaced by a lazily-loading variant.

Minimal example

// main.ts (entrypoint)
import { initLazyLoading } from '@itrocks/lazy-loading'

// Enable lazy loading for subsequently required modules
initLazyLoading()

// After this point, imports may be wrapped with lazy-loading behaviour
import { User } from './user.js'

async function run() {
  // Assuming the store and data source are configured elsewhere
  const user = new User()

  // When first accessed, `user.profile` will be loaded from the data source
  const profile = await user.profile
}

run().catch(console.error)
// user.ts
import { AnyType, Type } from '@itrocks/class-type'
import { store }         from '@itrocks/store'

@store({ /* store options */ })
export class User {
  id = 0

  // A lazily-loaded single object
  profile?: Profile
  profileId?: number

  // A lazily-loaded collection
  roles: Role[] = []
  roleIds?: number[]
}

export class Profile {
  id   = 0
  name = ''
}

export class Role {
  id   = 0
  name = ''
}

In this example, the User class is annotated with a store decorator from @itrocks/store. Once initLazyLoading() has been called, the exported User class is replaced with a subclass whose profile and roles properties are:

  • defined as asynchronous getters that
    • read profileId / roleIds when present and load the referenced objects from the configured data source, or
    • fall back to reading the related objects/collections directly from the store;
  • writable: once you explicitly assign user.profile or user.roles, the value is stored on the instance and will no longer trigger a reload on access.

The convention is that each lazily-loaded property may have a matching identifier field:

  • propertypropertyId
  • property (collection) ↔ propertyIds

Example with manual class initialization

If you do not want to use the global module loader hook, you can call initClass() manually on your entity classes.

import { initClass } from '@itrocks/lazy-loading'

class Order {
  id = 0

  customer?: Customer
  customerId?: number
}

class Customer {
  id   = 0
  name = ''
}

// Create a lazily-loading variant of Order, if applicable
const LazyOrder = initClass(Order) ?? Order

async function process(orderId: number) {
  const order = new LazyOrder()
  order.id = orderId

  // First access will load `customer` based on `customerId`
  const customer = await order.customer
}

Here, initClass(Order) inspects the Order metadata and, when it finds lazily-loadable properties, returns a subclass that contains the lazy getters. If the class cannot be configured for lazy loading (for instance, because it is not associated with a store), initClass() returns undefined and you can fall back to using the original class.

API

const PROTECT_GET: unique symbol

Symbol used as a metadata key on lazily-loaded properties.

This symbol is exposed for advanced integrations with custom persistence/ORM layers. Typical usages include:

  • detecting whether a property is currently under lazy-loading protection (its value is managed by the lazy getter), and
  • deciding whether a persistence layer should re-fetch or reuse the in-memory value when saving.

Most applications do not need to access PROTECT_GET directly. The @itrocks/framework integration uses it behind the scenes when implementing database transformers.


type PropertyDescriptorWithProtectGet

type PropertyDescriptorWithProtectGet = PropertyDescriptor & ThisType<any> & {
  [PROTECT_GET]?: true
}

Helper type describing property descriptors that may carry the PROTECT_GET marker. This is primarily useful when extending or customizing the lazy-loading behaviour at a low level, for example when defining additional decorators or property interceptors.


function initClass<T extends object>(classType: Type<T>): Type<T> | undefined

Analyses the given class and returns a lazily-loading subclass when at least one property can be configured for lazy loading.

The returned subclass extends classType and overrides qualifying properties with asynchronous getters/setters that:

  • lazily load a single related object based on propertyId,
  • lazily load a collection based on propertyIds, or
  • lazily fetch related data from the configured store when no id field exists.

Parameters

  • classType – the entity class to inspect and wrap with lazy-loading behaviour.

Return value

  • Type<T> | undefined – a subclass of classType that implements lazy loading, or undefined if the class cannot be configured (for example, because no store is registered for it).

Example

import type { Type } from '@itrocks/class-type'
import { initClass } from '@itrocks/lazy-loading'

function withLazyLoading<T extends object>(classType: Type<T>): Type<T> {
  return initClass(classType) ?? classType
}

// Later, when registering entities
const LazyUser = withLazyLoading(User)

function initLazyLoading(): void

Installs a global hook on Node's module loader so that exported entity classes are transparently replaced by lazily-loading subclasses.

Once initLazyLoading() has been called, every subsequent require() or import is inspected. For each exported value that looks like an entity class (based on @itrocks/store metadata):

  • its properties are analysed via reflection,
  • eligible properties are replaced with lazy getters/setters, and
  • the module export is updated to reference the new subclass.

The hook also handles deferred types, so that circular or late-bound dependencies between entities can still benefit from lazy loading.

Call this function only once, early in your application's startup sequence, before loading the bulk of your entity modules.

Typical use cases

  • Add transparent lazy loading to entity relationships (one-to-one or one-to-many) without changing your domain model code.
  • Defer loading of large collections until they are actually accessed, reducing memory usage and database round-trips.
  • Work with simple Id/Ids fields while exposing rich object graphs to the rest of the application.
  • Integrate with @itrocks/framework, which automatically enables lazy loading and uses PROTECT_GET when reading from and writing to the database.
  • Implement advanced persistence strategies by inspecting PROTECT_GET metadata when deciding how and when to persist entity properties.