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

rtk-entity-updater

v0.1.0

Published

A dev-time code generator that keeps RTK Query cache in sync when entities are updated or deleted outside of a query re-fetch.

Readme

rtk-entity-updater

A dev-time code generator that keeps RTK Query cache in sync when entities are updated or deleted outside of a query re-fetch.

The generator reads your RTK Query API file, walks the TypeScript types to discover every entity and where it can appear in query responses, and emits a typed helper file. At runtime those helpers find every copy of an entity across all active cache entries and patch or remove it in one dispatch.

How it works

At build time you run the generator once (or whenever the API changes). It outputs two files into your project:

  • generated/exampleApi.ts — typed updateEntity, deleteEntity, and setupMutationListeners functions bound to your specific entities and query shapes; also exports reducerPath detected from your API definition
  • generated/utils.ts — the runtime traversal engine (copied verbatim; not bundled into the library itself)

At runtime updateEntity and deleteEntity dispatch Redux thunks that traverse the RTK Query cache using the pre-computed shape maps and patch every matching occurrence via Immer. setupMutationListeners wires up RTK Query listener middleware to do this automatically after PATCH/PUT mutations complete.

Installation

npm install --save-dev rtk-entity-updater

Peer dependencies: @reduxjs/toolkit, immer, typescript.

Usage

1. Generate the helper file

Call generate from a script (e.g. scripts/generateApi.ts) and point it at your RTK Query API file:

import { generate } from 'rtk-entity-updater';

await generate('./src/store/exampleApi.ts', './src/store/generated/exampleApi.ts');

Add it as an npm script:

"scripts": {
  "generate": "tsx scripts/generateApi.ts"
}

Run it whenever your API types change:

npm run generate

2. Wrap the API reducer

In your store setup, wrap the RTK Query reducer so it can handle the cache-patch actions:

import { configureStore } from '@reduxjs/toolkit';
import { exampleApi } from './exampleApi';
import { wrapApiReducer } from './generated/utils';

export const store = configureStore({
  reducer: {
    [exampleApi.reducerPath]: wrapApiReducer(exampleApi.reducer),
  },
  middleware: (getDefault) => getDefault().concat(exampleApi.middleware),
});

3. Set up mutation listeners (optional)

If you want PATCH/PUT mutations to automatically sync the cache on success, add listener middleware and call setupMutationListeners:

import { configureStore, createListenerMiddleware } from '@reduxjs/toolkit';
import { exampleApi } from './exampleApi';
import { setupMutationListeners } from './generated/exampleApi';
import { wrapApiReducer } from './generated/utils';

const listenerMiddleware = createListenerMiddleware();
setupMutationListeners(listenerMiddleware, exampleApi);

export const store = configureStore({
  reducer: {
    [exampleApi.reducerPath]: wrapApiReducer(exampleApi.reducer),
  },
  middleware: (getDefault) =>
    getDefault().concat(exampleApi.middleware, listenerMiddleware.middleware),
});

4. Update and delete entities

Dispatch the generated thunks anywhere you have access to the Redux store:

import { updateEntity, deleteEntity } from './generated/exampleApi';

// updater is typed as (entity: Draft<User>) => void — no casting needed
store.dispatch(updateEntity('User', userId, (user) => {
  user.displayName = 'New Name';
}));

store.dispatch(deleteEntity('Comment', commentId));

Both functions find every copy of the entity across all active query cache entries and apply the change atomically, so list queries, detail queries, and nested occurrences (e.g. a User embedded in a Repository.owner field) all stay consistent.

Entity detection

The generator identifies an entity type by these criteria:

  • It is a named TypeScript type alias (not an inline object or interface)
  • It resolves to a non-array object with at least one property
  • It has a field whose normalized name (lowercased, non-alpha stripped) is id or {TypeName}id

Only PATCH and PUT mutations are included in the auto-sync map. POST mutations create new entities and don't update existing cache entries.

Generated file shape

// Typed overloads — entityType and updater callback are strictly typed per entity
export function updateEntity(entityType: 'User', id: string | number, updater: (entity: Draft<User>) => void): ...;
export function updateEntity(entityType: 'Post', id: string | number, updater: (entity: Draft<Post>) => void): ...;
// ...

export function deleteEntity(entityType: 'User', id: string | number): ...;
// ...

// reducerPath detected from your createApi / injectEndpoints call
export const reducerPath = 'api' as const;

Requirements

  • TypeScript 5+
  • @reduxjs/toolkit 2+
  • immer 9+