← Back to Chapters

NgRx Entity

? NgRx Entity

NgRx · Entity · State Management

? Quick Overview

NgRx Entity is a helper library that makes it easier to manage large collections of entities (like users, products, or posts) in the NgRx store. It provides adapters and utility methods for common CRUD operations so that you write less boilerplate and keep your state normalized, predictable, and easy to query.

? Key Concepts

  • Entity — A single item in your collection (e.g., one User record).
  • EntityState<T> — A normalized state shape that stores entities by ID and as an array of IDs.
  • EntityAdapter<T> — A set of helper methods to perform CRUD operations on the state.
  • Selectors — Auto-generated functions (like selectAll) to read entities from the store.
  • Normalized State — Data stored like a dictionary for fast lookups and updates.

? Syntax & Theory

To use NgRx Entity, you typically:

  1. Define a model interface for your entity (e.g., User).
  2. Extend EntityState<User> to describe the feature state.
  3. Create an EntityAdapter<User> using createEntityAdapter.
  4. Get an initial state via adapter.getInitialState().
  5. Use adapter methods like addOne, removeOne, and setAll inside a reducer.
  6. Expose selectors for reading data in components.

? Code Example: NgRx Entity for Users

? View Code Example
// Use NgRx Entity to manage a collection of User records in the store
    export interface User {
    id: string;
    name: string;
    email: string;
    }

    import { createEntityAdapter, EntityState } from '@ngrx/entity';
    import { createReducer, on } from '@ngrx/store';
    import { addUser, removeUser, loadUsersSuccess } from './user.actions';

    export interface UserState extends EntityState<User> {}

    export const adapter = createEntityAdapter<User>();

    const initialState: UserState = adapter.getInitialState();

    export const userReducer = createReducer(
    initialState,
    on(addUser, (state, { user }) => adapter.addOne(user, state)),
    on(removeUser, (state, { id }) => adapter.removeOne(id, state)),
    on(loadUsersSuccess, (state, { users }) => adapter.setAll(users, state))
    );

    export const { selectAll, selectEntities } = adapter.getSelectors();

? Live State Flow & Explanation

? What happens at runtime?

  • When addUser is dispatched, the reducer calls adapter.addOne(user, state), which adds the new user to the normalized collection and keeps IDs in sync.
  • When removeUser is dispatched, adapter.removeOne(id, state) removes the user from both the entity dictionary and its IDs list.
  • After a successful API call, loadUsersSuccess uses adapter.setAll(users, state) to replace the current collection with the loaded list.
  • Components typically use selectAll to get an array of all users, or selectEntities to get a dictionary keyed by ID.

? Common Use Cases

  • Managing large lists like users, products, posts, comments, or tasks.
  • Any feature where you frequently add, remove, or update individual items in the store.
  • Scenarios where you need fast lookup by ID and efficient updates.

✅ Tips & Best Practices

  • Keep reducers pure — never perform API calls or side effects inside reducers.
  • Use NgRx Entity for large collections to avoid manual CRUD logic.
  • Organize state into feature modules for better scalability and isolation.
  • Always handle API errors with dedicated *Failure actions in effects.
  • Use selectors to read data instead of directly accessing state slices in components.
  • Maintain a clear folder structure: actions, reducers, effects, selectors, and models.

? Try It Yourself

  • Create a User feature using NgRx Entity with add, remove, and load actions.
  • Write selectors to get:
    • All users (using selectAll).
    • A single user by ID (using selectEntities or a custom selector).
  • Experiment with additional adapter methods: updateOne, upsertOne, and upsertMany.
  • Extend the UserState with extra UI properties (like loading or error) using adapter.getInitialState({ ...extraProps }).