Angular NgRx Entity
Learn Angular NgRx Entity with EntityState, EntityAdapter, normalized state, CRUD operations, selectors, Signals integration, enterprise architecture, best practices, and interview questions.
Managing collections such as:
- Customers
- Orders
- Products
- Employees
- Accounts
- Transactions
is one of the most common requirements in enterprise Angular applications.
Without a standardized approach, reducers become repetitive with duplicate CRUD logic.
NgRx Entity solves this problem by providing utilities to manage collections in a normalized, efficient, and scalable way.
It dramatically reduces boilerplate code while improving performance and maintainability.
Table of Contents
- What is NgRx Entity?
- Why Use NgRx Entity?
- Entity Architecture
- Normalized State
- EntityState
- EntityAdapter
- CRUD Operations
- Entity Selectors
- Signals Integration
- Enterprise Banking Example
- Best Practices
- Common Mistakes
- Top 10 Interview Questions
- Interview Cheat Sheet
- Summary
What is NgRx Entity?
NgRx Entity is an extension of NgRx Store that simplifies managing collections of objects.
Instead of writing repetitive reducer logic for:
- Add
- Update
- Delete
- Replace
- Upsert
EntityAdapter provides optimized helper methods.
Why Use NgRx Entity?
Without Entity
- Duplicate reducer logic
- Manual array manipulation
- Slower lookups
- Difficult maintenance
With Entity
- Less boilerplate
- Faster CRUD operations
- Normalized data
- Efficient selectors
- Better scalability
Entity Architecture
flowchart LR
Component
Component --> Action
Action --> Reducer
Reducer --> EntityAdapter
EntityAdapter --> Store
Store --> Selectors
Selectors --> Component
Traditional State
Many beginners store data like this:
export interface CustomerState {
customers: Customer[];
loading: boolean;
}
Finding one customer requires searching the array.
customers.find(
customer =>
customer.id === id
);
Time complexity:
O(n)
Normalized State
NgRx Entity stores collections differently.
export interface CustomerState
extends EntityState<Customer> {
loading: boolean;
}
Internally
CustomerState
ids:
[1,2,3]
entities:
1 -> Customer
2 -> Customer
3 -> Customer
Lookups become much faster.
Entity State Architecture
flowchart TD
API
API --> EntityAdapter
EntityAdapter --> ids
EntityAdapter --> entities
ids --> Store
entities --> Store
Creating an Entity Adapter
import {
createEntityAdapter,
EntityAdapter,
EntityState
} from '@ngrx/entity';
export interface CustomerState
extends EntityState<Customer>{
loading:boolean;
}
Create adapter
export const adapter =
createEntityAdapter<Customer>();
Initial State
export const initialState =
adapter.getInitialState({
loading:false
});
Instead of manually creating arrays,
Entity creates the normalized structure.
CRUD Operations
Add One
adapter.addOne(
customer,
state
);
Add Many
adapter.addMany(
customers,
state
);
Update One
adapter.updateOne(
{
id:customer.id,
changes:customer
},
state
);
Remove One
adapter.removeOne(
customerId,
state
);
Remove Many
adapter.removeMany(
ids,
state
);
Set All
adapter.setAll(
customers,
state
);
Upsert One
adapter.upsertOne(
customer,
state
);
Updates existing records or inserts new ones.
CRUD Flow
flowchart LR
Action
Action --> Reducer
Reducer --> EntityAdapter
EntityAdapter --> Store
Reducer Example
export const customerReducer =
createReducer(
initialState,
on(
loadCustomersSuccess,
(state,{customers}) =>
adapter.setAll(
customers,
state
)
),
on(
addCustomerSuccess,
(state,{customer}) =>
adapter.addOne(
customer,
state
)
)
);
Notice how little code is required.
Entity Selectors
Entity generates selectors automatically.
const {
selectAll,
selectEntities,
selectIds,
selectTotal
}
=
adapter.getSelectors();
Available Selectors
| Selector | Description |
|---|---|
| selectAll | Returns all entities as an array |
| selectEntities | Returns entity dictionary |
| selectIds | Returns all IDs |
| selectTotal | Returns entity count |
Entity Selector Architecture
flowchart LR
Store
Store --> EntitySelectors
EntitySelectors --> Component
Sorting Entities
EntityAdapter supports sorting.
export const adapter =
createEntityAdapter<Customer>({
sortComparer:
(a,b)=>
a.name.localeCompare(
b.name
)
});
Now all entities remain sorted automatically.
Custom Entity ID
Default ID
id
Custom ID
createEntityAdapter<Customer>({
selectId:
customer =>
customer.customerNumber
});
Useful when APIs use different identifiers.
Signals Integration
Selectors work perfectly with Signals.
customers =
toSignal(
this.store.select(
selectAllCustomers
)
);
Template
@for(
customer of customers();
track customer.id
){
<div>
{{customer.name}}
</div>
}
Signals provide a clean reactive UI while Entity manages efficient collections.
Signals Architecture
flowchart LR
EntityStore
EntityStore --> Selector
Selector --> Signal
Signal --> Component
Enterprise Banking Example
A banking system stores:
- Customers
- Accounts
- Cards
- Loans
- Investments
- Transactions
Each collection contains thousands of records.
Architecture
flowchart TD
RESTAPI
RESTAPI --> EntityAdapter
EntityAdapter --> Store
Store --> EntitySelectors
EntitySelectors --> Signals
Signals --> Dashboard
Benefits
- Fast lookups
- Efficient updates
- Minimal reducer code
- Excellent scalability
Project Structure
store/
├── actions/
│
├── reducers/
│
├── selectors/
│
├── effects/
│
├── entity/
│
└── models/
Performance Best Practices
| Practice | Benefit |
|---|---|
| Use normalized state | Faster lookups |
| Use EntityAdapter | Less reducer code |
| Generate selectors | Better performance |
| Use immutable updates | Predictable state |
| Combine with Signals | Cleaner UI |
| Use OnPush | Faster rendering |
| Organize by feature | Better scalability |
| Use sorting only when needed | Lower processing cost |
Common Mistakes
Managing Arrays Manually
❌ Wrong
customers.push(customer);
Use
adapter.addOne();
Ignoring Entity Selectors
Don't manually search arrays.
Use generated selectors.
Mutating State
Never modify Store state directly.
Always return immutable state using EntityAdapter methods.
Storing Duplicate Data
Keep one normalized collection.
Compute derived values using Selectors.
Using Entity for Tiny Collections
Entity shines with medium and large collections.
For very small local lists, plain arrays may be sufficient.
Array vs NgRx Entity
| Feature | Array | NgRx Entity |
|---|---|---|
| CRUD Boilerplate | High | Very Low |
| Lookup Performance | O(n) | O(1) by ID lookup |
| Sorting Support | Manual | Built-in |
| Generated Selectors | No | Yes |
| Enterprise Applications | Moderate | Excellent |
Advantages
| Feature | Benefit |
|---|---|
| EntityAdapter | Less boilerplate |
| Normalized State | Faster lookups |
| CRUD Helpers | Cleaner reducers |
| Generated Selectors | Less code |
| Signals Integration | Modern Angular |
| Enterprise Ready | Highly scalable |
Top 10 NgRx Entity Interview Questions
1. What is NgRx Entity?
Answer
NgRx Entity is a library that simplifies managing collections in the NgRx Store using normalized state and helper methods for CRUD operations.
2. Why should we use NgRx Entity?
Answer
It reduces reducer boilerplate, improves lookup performance, provides generated selectors, and simplifies collection management.
3. What is EntityState?
Answer
EntityState<T> is an interface that stores entities in a normalized structure using an ids array and an entities dictionary.
4. What is an EntityAdapter?
Answer
An EntityAdapter is a helper object that provides methods such as addOne, addMany, updateOne, removeOne, setAll, and upsertOne.
5. What is normalized state?
Answer
Normalized state stores entities in a dictionary keyed by unique IDs, along with a separate list of IDs, avoiding duplicate data and enabling efficient lookups.
6. What selectors does Entity generate?
Answer
Entity automatically provides selectors including:
selectAllselectEntitiesselectIdsselectTotal
7. Can Entity be used with Signals?
Answer
Yes. Entity selectors can be converted into Signals using toSignal(), allowing Angular templates to consume normalized state reactively.
8. Can Entity sort data automatically?
Answer
Yes. An EntityAdapter can be configured with a sortComparer function to maintain sorted collections automatically.
9. What are common NgRx Entity mistakes?
Answer
- Managing arrays manually
- Ignoring generated selectors
- Mutating state
- Storing duplicate data
- Using Entity where a simple array is sufficient
10. When should you use NgRx Entity?
Answer
Use NgRx Entity whenever your application manages medium or large collections with frequent CRUD operations, especially in enterprise applications.
Interview Cheat Sheet
| Requirement | Recommended Solution |
|---|---|
| Collection Management | NgRx Entity |
| Normalized State | EntityState |
| CRUD Operations | EntityAdapter |
| Read Collections | Generated Selectors |
| Reactive UI | Signals |
| Change Detection | OnPush |
| Async Data | Effects |
| Global State | Store |
| Enterprise Apps | NgRx Entity + Store |
| Large Collections | Entity + Selectors |
Summary
NgRx Entity is one of the most valuable extensions to NgRx for managing collections efficiently. By storing data in a normalized format and providing an EntityAdapter with built-in CRUD operations and generated selectors, it significantly reduces boilerplate while improving performance and maintainability. Combined with Signals, Selectors, OnPush Change Detection, and Effects, NgRx Entity forms a robust foundation for scalable enterprise Angular applications.
Key Takeaways
- ✅ NgRx Entity simplifies collection management.
- ✅
EntityStatestores normalized data usingidsandentities. - ✅
EntityAdapterprovides optimized CRUD operations. - ✅ Generated selectors reduce repetitive code.
- ✅ Normalized state enables fast lookups by ID.
- ✅ Use
sortComparerfor automatic sorting. - ✅ Integrate Entity selectors with Angular Signals.
- ✅ Keep reducers immutable and concise.
- ✅ Use Entity for medium and large collections.
- ✅ NgRx Entity is a frequently asked enterprise Angular interview topic.
Next Article
➡️ 101-NgRx-ComponentStore-QA.md