Angular: Build a CRUD Application With NgRx
Build a simple course management system in Angular

Introduction
In my previous post, I explained the fundamental concepts of NgRx and how they fit in together. Now, it’s time to build a mini-CRUD application with NgRx as the state management system.
If you are brand new to the world of NgRx, I suggest you check out my previous post before attempting this tutorial. I won’t be going into detail about the underlying NgRx concepts in this article.
Project Details
Throughout this article, we will be building a simple course management system. As shown below, you will be able to perform all the CRUD operations on the course entity via this simple web application.

Project structure
As the following figure illustrates, our application will consist of two primary modules, namely App
and Course
. The course module, in turn, will have two custom components, namely Course List
and Create Course
.

REST API
In general, an Angular application interacts with a REST API to perform CRUD operations on data.
Therefore, I implemented a simple REST API in Spring Boot that exposes the below endpoints. We will use this API to connect from the Angular application and carry out data operations.
// Retrieve all courses
GET http://localhost:8080/api/courses// Create a course
POST http://localhost:8080/api/courses// Delete a course
DELETE http://localhost:8080/api/courses/{courseId}// Update a course
PUT http://localhost:8080/api/courses/{courseId}
Full source code
You can find the complete source code of this sample application on GitHub. Please note that I have also added the executable JAR file (course-1.0.0-SNAPSHOT.jar) of the Spring Boot application (REST API) to the same repository.

NgRx Entity at a Glance
You have already come across most of the NgRx terms that I will use in this article. For example, store, effects, actions, selectors, and reducers. In this article, I will introduce a new NgRx library called NgRx Entity (@ngrx/entity
).
NgRx Entity helps us to manage various data entities in an application. For example, the Course
is an entity in our application. It takes the following format.
export interface Course {id: string;name: string;description: string;}
The NgRx Entity library makes it very easy to perform different operations (add, update, remove, select) on course objects stored in the application state. Let’s see how…
EntityState interface
The entity library provides a set of tools to make our life easier with NgRx. The first and foremost is the EntityState
interface. The shape of theEntityState
looks like the below.
interface EntityState<V> {
ids: string[];
entities: { [id: string]: V };
}
We have to use EntityState
to declare the interface for our courses
state.
import { EntityState } from '@ngrx/entity';export interface CourseState extends EntityState<Course> { }
When EntityState
is used, the courses
state will take the following format.

As you can see, it maintains an array of course IDs and a dictionary of course objects. There are two primary reasons we maintain a list of IDs and a dictionary of entities as opposed to just maintaining an array of entities:
- We want to make looking up a specific entity fast. If you wanted to just select one course from the store, using the entities dictionary is much faster than searching through an array
- We also want to maintain the order of the list. This is especially important if you want to keep the list sorted!
Entity adapter
Entity adapter is another tool that goes hand-in-hand with EntityState
. It provides a bunch of helper methods that make it very easy to perform various operations on data stored in EntityState
.
These helper methods make reducers simple, expressive, and consistent. You can create an entity adapter in the following way.
import { createEntityAdapter } from '@ngrx/entity';
const courseAdapter = createEntityAdapter<Course>();
The following are some of the very useful methods exposed by the adapter to interact with the state.
addOne
: Add one entity to the collection.addMany
: Add multiple entities to the collection.addAll
: Replace current collection with provided collection.removeOne
: Remove one entity from the collection.removeMany
: Remove multiple entities from the collection, by I or by the predicate.removeAll
: Clear entity collection.updateOne
: Update one entity in the collection.updateMany
: Update multiple entities in the collection.upsertOne
: Add or update one entity in the collection.upsertMany
: Add or update multiple entities in the collection.map
: Update multiple entities in the collection by defining a map function, similar toArray.map
.
Setting Up the Project
Software versions used
- Angular CLI: 8.0.1
- Node: 11.6.0
- Angular: 8.0.2
- NgRx: 8.6.0
- Bootstrap: 4.4.1
Project initialization
Step 1: Execute the below command and create a new project.
ng new angular-ngrx-example
Step 2: We will use Bootstrap to add styles to our application. You can install Bootstrap with the below command.
npm install bootstrap --save
Step 3: Import Bootstrap by updating the angular.json
file as shown below.
Step 4: Install NgRx dependencies.
npm install @ngrx/{store,effects,entity,store-devtools,schematics} --save
Adding NgRx Support to Root Module
Execute the below schematics command to generate the initial state management and register it within the app.module.ts
.
ng generate @ngrx/schematics:store State --root --statePath store/reducers --module app.module.ts
After the above command, your project folder structure should look like the below.

Following is the content of the index.ts
file. Please note that I made a couple of minor changes to the auto-generated file. For example, I changed the State
interface to AppState
for the sake of clarity.
The NgRx schematics command will also update the app.module.ts
file. Following is the updated content of this file.
Creating and Setting Up the “Course” Feature Module
Generating the “Course” module
As stated earlier, our application consists of two major modules, namely App
and Course
. Now is the time to create the Course
module with the below command.
ng generate module course
The aforementioned command will create a sub-folder named course
directly under the app
folder. In addition, a new file named course.module.ts
will be created and placed under the app/course
folder.
Following is the initial version of course.module.ts
file. Note that this file will be altered downstream to add NgRx support, declare components, and declare service providers.
Defining the “Course” model
As the next step, you have to define the model interface that represents the Course
entity. Create a file called course.model.ts
and place it under the app/course/model
folder. The content of this file should be as follows.
Defining the Service class
Service is used to interact with the REST API and perform data operations. In order to define the service class, create a file named course.service.ts
and place it under the app/course/services
folder.
The content of this file should be as follows.
As you can see, it has methods to retrieve, create, update, and delete Course
entities via the REST API. Once the service class is defined, you have to register it in the course.module.ts
file as shown below.
The following figure illustrates the folder structure of our application at this point.

Adding NgRx Artifacts to “Course” Module
As the next step, you have to define actions, reducers, effects, and selectors and attach to the course
module. These artifacts will be created inside a directory called store
which in turn is located under the app/course
directory.
Defining NgRx actions (course.actions.ts)
Special notes:
loadCourses
,createCourse
,deleteCourse
, andupdateCourse
are self-explanatory actions which are dispatched by the components. However,coursesLoaded
is a special action that will be dispatched by the effect in order to inform the store that the courses were loaded successfully.- The
updateCourse
action accepts a payload of type{update: Update<Course>}
.Update
is an auxiliary type provided by NgRx Entity to help model partial entity updates. This type has a propertyid
that identifies the updated entity, and another property calledchanges
that specifies which modifications are being made to the entity.
Defining NgRx reducers (course.reducers.ts)
Special notes:
- The below code snippet defines the
Course
state by extending theEntityState
. As we discussed before,EntityState
maintains a list of IDs and a dictionary of entities. In addition to those two properties, we are here defining a custom property calledcoursesLoaded
. This property is primarily used to indicate whether the courses have been already loaded into the state.
export interface CourseState extends EntityState<Course> { coursesLoaded: boolean;}
- The below code snippet creates an
Entity Adapter
that provides the helper functions.
export const adapter: EntityAdapter<Course> = createEntityAdapter<Course>();
- The initial state is defined as follows. Entity adapter provides a helper function to obtain the initial state. As you can see, we are setting the
coursesLoaded
property tofalse
initially.
export const initialState = adapter.getInitialState({ coursesLoaded: false});
- The following code line exports a couple of predefined selectors provided to us by the adapter. These selectors will be used by our custom selectors. (We will look into this when we define our selectors.)
export const { selectAll, selectIds } = adapter.getSelectors();
Defining NgRx selectors (course.selectors.ts)
Special notes:
- Here, we are using the
selectAll
predefined selector to retrieve all the course entities as an array. - The
areCoursesLoaded
selector is used to check whether the courses have been already loaded into the state. This selector uses thecoursesLoaded
custom property we defined underCourseState
.
Defining NgRx effects (course.effects.ts)
Special notes:
createCourse$
, deleteCourse$
, and updateCourse$
effects are self-explanatory. They simply invoke the corresponding REST endpoint and perform the operation.
These effects do not map the incoming action to a new action type, which is why {dispatch: false}
config is used.
However, loadCourses$
has a special behavior. It accepts the actions of type loadCourses
and once the courses are retrieved via the REST API, it maps the response to a new action type called coursesLoaded
.
The retrieved list of courses is passed into the coursesLoaded
action.
Registering the NgRx artifacts in the Course module
After the NgRx artifacts are defined, update the course.module.ts
file as shown below to add the State
support.
Special notes:
- The below code line creates a dedicated slice (
courses
) in the application state for the course module and attaches the reducers to it.
StoreModule.forFeature('courses', courseReducer),

- The following code line registers the effects in the course module state.
EffectsModule.forFeature([CourseEffects])
Now that that’s out of the way, your project folder structure should look like the below at this stage.

Creating Components and Defining Routes
As we discussed earlier, our application is made up of two main modules, namely App
and Course
. The course module consists of two components, namely courses-list
and create-course
.
Our next step is to create these two components and define the corresponding routes. Note that the courses-list
and create-course
directories will be created under the app/course/component
directory.
Defining “courses-list” component
Template: courses-list.component.html
.
Component: courses-list.component.ts
.
Special notes:
- This component is responsible for facilitating the list, updating, and deleting operations.
Defining “create-course” component
Template: create-course.component.html
.
Component: create-course.component.ts
.
Declaring the components in the Course module
You have to declare the above components in the course.module.ts
file.
Configuring routes
Now is the time to define the routes and associate corresponding components with those routes. This has to be done in the app.module.ts
as shown below.
Special notes:
CoursesListComponent
uses a resolver to fetch data. A route resolver ensures that the data is available to use by the component before navigating to a particular route. In this instance, the resolver is responsible for retrieving the courses list prior to completing the navigation to/courses
.
Implementing the route resolver (course.resolver.ts)
Special notes:
- The
areCoursesLoaded
custom selector is used to check whether the data has already been loaded into the state. - The
loadCourses
action is dispatched only if the data is not already available in the state. - The operator chain will not let a value pass through to the subscriber until the
coursesLoaded
flag is set totrue
. As a result, the application will not be navigated to the/courses
route until the courses are successfully loaded.
Defining the router outlet
As the final step, you have to define the router outlet in the app.component.html
.
At this stage, your folder structure should look like the below.

Configuring a Proxy to Access the REST API
As mentioned at the start of this article, we are using a simple REST API written in Spring Boot to connect from the Angular application.
The Spring Boot application runs on localhost:8080
whereas the Angular application runs on localhost:4200
. This mismatch will cause a Cross Origin Resource Sharing (CORS) error when the Angular application tries to access the REST API. To overcome this issue we have to create a proxy.
Creating a proxy file
Create a file called proxy.conf.json
inside the project’s root folder (same level where package.json
file exists), and add the below content to it.
Registering the proxy file
In the CLI configuration file, angular.json
, add the proxyConfig
option to the serve
target:
Running the Application
The application should be started as a two-step process. You have to first start the Spring Boot application (REST API) and then the Angular application.
Running the Spring Boot application (REST API)
The Spring Boot application is packaged into an executable JAR file with the name course-1.0.0-SNAPSHOT.jar
and placed here (GitHub).
Note that you have to have Java 8 installed on your system to execute this JAR file. If Java 8 is installed, you can execute the below command and start the application.
java -jar {path_to_the_jar_file}/course-1.0.0-SNAPSHOT.jar
You should see the below log if the application started successfully.
Running the Angular application
The Angular application can be started by executing the below command.
ng serve
When the application is started successfully, navigate to http://localhost:4200/courses
from your browser and you should see the below screen.

Understanding the Execution Flow
Retrieve courses

Create course

Special note: The key thing to note is that the reducer updates the state with the new data (in turn, the UI will be updated), even before the effect invokes the API and actually creates a record in the server.
Update course

Special note: Again, the reducer updates the state with the updated course data (in turn, the UI will be updated), even before the effect invokes the API and actually updates the relevant record in the server.
Delete course

Special note: Similar to creating a course and updating a course, the reducer removes the relevant course information from the state (in turn, the UI will be updated), even before the effect invokes the API and remove the relevant record in the server.
Optimistic UI
Optimistic UI is a pattern that you can use to simulate the results of a state mutation and update the UI even before receiving a response from the server.
In this particular application, we are following the same pattern. As explained above, when creating, updating, and deleting a course, the state and the UI are updated even before receiving a response from the REST API.
Conclusion
The prime objective of this story was to provide a step-by-step by guide to build an NgRx-based Angular application.
As explained in the previous section, we have used the optimistic UI pattern to implement this mini system.
In my next article, I’m planning to explain how error handling can be done when using the optimistic UI pattern for NgRx-based applications.