This week and the coming one, I'll be writing a new series of articles on how to develop a full-stack application with Angular v8 on the client-side backed up with ASP.NET Core 2.2 and Node.js respectively.
The first two installments of this series will cover building an Angular app and connecting it to an ASP.NET Core Web API app. The backend Web API uses SQLite database for storage and also authenticates users by means of JWT (JSON Web Tokens).
The second two installments will look at using the same Angular app, however this time, connecting it to a backend Node.js API app (most likely using Nest.js). The backend API uses a PostgreSQL database for storage and also authenticates users by means of JWT.
Angular is created and maintained by Google and is mainly used for developing Single Page Apps (SPA). On the other hand, ASP.NET Core is created and maintained by Microsoft and can be used to design and build a RESTful Web API that serve client-side apps, including and not limited to, Angular apps.
Today, I'll start building the Angular app to track movies that I've watched and will be watching in the future. The Angular app allows me to perform all CRUD (Create Read Update and Delete) operations on the movies. From the backend side, I will build an ASP.NET Core Web API. For the time being, it will provide a RESTful Endpoint to perform all the CRUD operations on movies and connect to SQLite database. Moreover, I will show you how to use NSwag to enable the Web API to generate Swagger API documentation. With the help of NSwagStudio, we can generate a TypeScript Service based on the Web API Endpoint and embed inside the Angular app. This way, there is no need to generate this code and it saves time.
In brief, Swagger generates documentation for the Web API that allows any consumer to know what are the capabilities of this API and how to call the different endpoints. You can read more about it here on the Swagger Website.
NSwag Nuget Package gives you the APIs needed to configure your Web API to generate Swagger API Documentation. You can read more about it here on NSwag Github.
NSwagStudio is a Desktop app provided by the NSwag team to allow you to do code generation. They also offer a command-line too. You can read more about it here NSwagStudio.
Let's start building the backend Web API.
Building the ASP.NET Core Web API
To start building Web APIs with ASP.NET Core, make sure you have the following tools and frameworks locally installed on your machine.
I will be developing this app on a Windows machine. Feel free to use Macbook or any other machine as per your preference.
In the remaining part of this section, we will go through a step by step guide on how to develop the Web API.
The source code for the backend Web API can be found on this Github repo.
Step 1
Open Visual Studio 2019, locate and click the Create a new Project button as you can see below.
Step 2
Search for the ASP.NET Core Web Application template and then click on Next button.
Step 3
Provide a Project name, Location and hit Create button.
Step 4
Make sure you select the Empty Application Type and hit Create.
This creates a new Empty ASP.NET Core application:
The scope of this article is limited to just using ASP.NET Core without going into great depth. If you want more on ASP.NET Core you may visit the official ASP.NET Core documentation.
Step 5
Add a new class named Movie.cs inside the Model folder at the root of the application as follows:
This will be our model class for a single Movie. I care to record the title of the movie, when I watched it, the movies' genre and my rating on a scale of 1 - 5.
Step 6
Now that the Movie model class is created, let's add an Entity Framework Core Database Context that will act as a gateway between the application and the database.
The MovieTrackerContext supports one table for now the Movies table.
In addition, it overrides the OnModelCreating event handler to instruct EF Core engine on how to create the table inside the database. It customizes the column settings. For instance, the code above defines the column Id to be an Auto Increment column. When creating a new record, the database engine will generate a new and unique Id and assign it to this column.
Also, inside the OnModelCreating we are seeding some data so that we don't get an empty database.
Finally, install the following SQLite NuGet packages for the application to build and function properly.
Install-Package Microsoft.EntityFrameworkCore.Sqlite
Install-Package Microsoft.EntityFrameworkCore.Sqlite.Design
To read more about Entity Framework Core have a look at their official documentation website.
Step 7
Now that the MovieTrackerContext is ready, let's create a migration and update the database accordingly.
Open the Package Manager Console and issue the following command to generate a migration that EF Core engine will use to create the database and tables inside accordingly.
Add-Migration InitialCreate
This creates a new migration file that instructs EF Core on how to create the Movie table and how to seed it with some data. Locate the Migrations folder at the root of the application (that was automatically created for you by EF Core engine) and have a look at the migration file.
A migration contains two important methods: Up and Down. The Up method is called when updating the database and running the migration. While the Down method is called when reverting back from a migration to clean up.
Step 8
Before you can generate the database structure, you have to introduce the MovieTrackerContext to the EF Core engine by registering the context class inside the ASP.NET Core Dependency Injection internal system.
Locate the Startup class and add the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MovieTrackerContext>(options => options.UseSqlite(Configuration.GetConnectionString("LocalDb")));
}
The ConfigureServices method is called by the runtime and it gives the developer a chance to register services inside the Dependency Injection system.
The code registers the MovieTrackerContext as a service by specifying the exact database provider to connect to via a Connection String.
The Connection String LocalDb is defined inside the appsettings.json file located at the root of the application.
"ConnectionStrings": {
"LocalDb": "Data Source=movietracker.db"
}
The connection string specifies the name of the SQLite database that will be created later at the root of the application folder.
The Configuration object is also defined inside the Startup class as follows:
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
The Configuration objects gives you access to all configuration information registered in the application.
Step 9
Let's create the database, create the Movies table and finally seed the data by running this command inside the Package Manager Console:
Update-Database
If you want to query and manipulate the data inside the SQLite database inside Visual Studio, you need to install the SQLite/SQL Server Compact Toolbox Visual Studio extension.
Step 10
Now that the Database is ready, let's introduce the MovieTrackerService that contains all the functionality exposed by the Web API endpoints.
Create a new MovieTrackerService.cs file inside the Services folder (create if it doesn't exist) at the root of the application folder.
Inside the file, add the following contract or interface to define what methods are going to be available on the service:
This is a typical set of CRUD operations that a Web API can implement.
Next, add a new MovieTrackerService class and let's see how each of the methods above are implemented.
public MovieTrackerService(MovieTrackerContext context)
{
this._context = context;
}
First of all, the MovieTrackerService requests an instance of the MovieTrackerContext via the constructor. The ASP.NET Core searches for the MovieTrackerContext inside its own Dependency Injection system and creates an instance of the MovieTrackerContext and provides it to the MovieTrackerService. This is a typical example on how Inversion of Control (IoC) works.
public async Task<List<Movie>> GetMovies() => await this._context.Movies.ToListAsync();
The GetMovies() method returns all the data inside the Movies table.
public async Task<Movie> GetMovie(int id) => await this._context.Movies.Where(m => m.Id == id).FirstOrDefaultAsync();
The GetMovie(int id) method queries for a Movie by Id parameter. The code is constructing a LINQ query to retrieve the data.
public async Task<Movie> CreateMovie(Movie movie)
{
await this._context.Movies.AddAsync(movie);
await this._context.SaveChangesAsync();
return movie;
}
The CreateMovie(Movie movie) method adds a new Movie to the database and saves the changes. In addition, it returns the new movie created.
public async Task DeleteMovie(Movie movie)
{
this._context.Movies.Remove(movie);
await this._context.SaveChangesAsync();
}
The DeleteMovie(Movie movie) method deletes an existing movie from the database and saves the changes.
The SearchMovies(string term) method searches for movies given a search term criteria. It tries to find movies by locating the search term inside the movie title and genre.
public async Task UpdateMovie(Movie movie)
{
this._context.Entry(movie).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
Finally, the UpdateMovie(Movie movie) method updates an existing movie by setting its State to a value of EntityState.Modified and then it saves the changes.
The bulk of the Web API implementation is done in this service.
Step 11
An ASP.NET Core Web API exposes its function by implementing a Controller. Let's add the MovieTrackerController as follows:
The MovieTrackerController is decorated with the Route attribute specifying the Endpoint that client-side apps will use to get access to this Web API.
In practice, a client-side app requests the URL /api/movietracker
to access this Web API.
In addition, the MovieTrackerController inherits from the Controller base class that provides some boilerplate methods that can be used inside the controller.
The controller starts by requesting an instance of the MovieTrackerService via constructor injection. Once again, the ASP.NET Core Dependency Injection system kicks in and provides an instance of this service to the controller.
The controller then defines all actions on this endpoint that can be accessed by the client-side app. For example, let's have a look at the SearchMovies action:
// GET: api/<controller>/search/{title}
[HttpGet("search/{term?}")]
public async Task<ActionResult<IEnumerable<Movie>>> SearchMovies(string term)
{
return await this._service.SearchMovies(term);
}
This action is only accessed by an HTTP Get request in the form of /api/movietracker/search/{string to search for}
.
The method redirects the call to the MovieTrackerService.SearchMovies() method passing to it the parameters needed.
If you look at the rest of action methods in this controller, they all follow the same strategy by redirecting calls to the MovieTrackerService methods. This is a good design guideline to keep your controllers thin and centralize the business logic inside the services.
Last but not least, we need to register the MovieTrackerService inside the ASP.NET Core Dependency Injection system inside the Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MovieTrackerContext>(options => options.UseSqlite(Configuration.GetConnectionString("LocalDb")));
services.AddScoped<IMovieTrackerService, MovieTrackerService>();
}
The MovieTrackerService is registered as a Scoped service meaning that one instance of this service is used within a single HTTP Request. For instance, if two controllers request this service, the same instance of the service is passed to both of them.
Step 12
Let's add support for Swagger API documentation to our Web API by first installing the following NuGet package from the Package Manager Console window:
Install-Package NSwag.AspNetCore
Then, open the Startup class and register the Swagger service inside the ConfigureServices method as follows:
The Swagger service registration allows you to personalize the Swagger API Documentation page.
Then, tell the ASP.NET Core engine that this application should generate Swagger API Documentation, locate the Configure method and register the Swagger API middlewares:
// Register the Swagger generator and the Swagger UI middlewares
app.UseOpenApi();
app.UseSwaggerUi3();
The Configure method is called by the ASP.NET Core runtime and allows the developer to customize the ASP.NET Core Pipeline by registering Middlewares. You can read more about ASP.NET Core Middlewares.
Step 13
Finally, let's add ASP.NET Core MVC engine into the application and run our Web API in the browser. ASP.NET Core MVC is itself built as a service hence it needs to be registered first before using it.
Start by registering the ASP.NET Core MVC services in the Dependency Injection system inside ConfigureServices method:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Then, register the ASP.NET Core MVC Middleware inside the Configure method as follows:
app.UseMvc();
Now we have a complete ASP.NET Core Web API application. Let's give it a shot and try to access it inside Postman requesting the URL:
GET /api/movietracker/
And here's the result:
The application is up and running and ready to serve the Angular app! The rest of the operations Create, Update and Delete a Movie, we will be testing throughout the Angular app soon.
Building the Angular client-side App
To start building the Angular app, make sure you have the following tools and frameworks locally installed on your machine.
Angular CLI v8.x What's new in Angular CLI 8
Node.js 10.x Node.js Download
Visual Studio Code (or any other editor of choice) [VS Code](https://code.visualstudio.com/
In the remaining part of this section, we will go through a step by step guide on how to develop the Angular app and connect it to the backend Web API we just developed.
The source code for the Angular app can be found on this Github repo.
Step 1
Create an Angular app by running this command on a terminal window:
ng new movie-watcher-client --style=scss --routing=true`
The above command uses the Angular CLI command to create and scaffold an Angular app.
Step 2
Add the Angular Material module as we are going to use some of the components and styles provided by this library. Run the following command:
ng add @angular/material
Step 3
Let's change the Angular Material theme in our app. Locate the angular.json file at the root of the application. Then go to the architect\build
and architect\test
nodes and change the Angular Material theme URL to match the one below:
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css"
Step 4
Let's separate all the modules we want to import from Angular Material into their own module. This way we can reference it easily in other modules instead of spreading around import statements from the Angular Material module.
Run the following command to create a new module:
ng generate module app-material
This command creates a new app-material.module.ts
file inside a newly created folder located at src\app\app-material\
folder.
Let's import the needed Angular Material modules for this app inside the app-material.module.ts
as follows:
Step 5
Create a new module to hold all the related components for the Movie Tracker module. Run the following command to create the movie-tracker.module.ts
:
ng generate module movie-tracker --routing=true --module=app.module
The command generates a new module inside a new folder \src\app\movie-tracker
.
Step 6
Let's generate the movie-tracker.service.ts
by using the NSwagStudio application. Start by publishing the ASP.NET Core Web API to a folder on the local file system.
Before actually publishing the Web API, open the MovieTrackerController class and add the ApiConventionType attribute at the class level as follows:
[ApiConventionType(typeof(DefaultApiConventions))]
public class MovieTrackerController : Controller
This attribute is used by Swagger to infer all possible HTTP Responses that a specific Endpoint action might return. You will see this later once we generate the Angular service using NSwagStudio.
Let's focus on publishing the application by following the steps below:
Open the ASP.NET Core app inside Visual Studio.
Right-click the project name and select
Publish
Locate the Start button and click it
Select the Folder as a publish target. Visual Studio automatically specifies the publish
folder inside the application path. You may change it or keep it, what matters is to remember this path for later.
Then, locate the Create Profile button in the bottom right side of the window.
Locate and click on the Publish button to start the publishing process.
Step 7
Open the NSwagStudio desktop app:
You specify the source Web API on the right side of the app. While on the left side you specify the output to be generated.
Make sure you select the following under the Input section:
- Select the Runtime to be NetCore22
- Select the tab ASP.NET Core via API Explorer
- Under the Assembly tab select the main DLL of the ASP.NET Core Web API, in our case it is the following path
D:\Projects\Playground\MovieTracker\Publish\MovieWatcher.Server.dll
- Under the Path to search for referenced assembly files paste in the path for the publish folder, in our case it is the following path
file://D:/Projects/Playground/MovieTracker/Publish
Make sure you select the following under Output section:
- Select the Template to be Angular
- Select the RxJs Version to be 6.0
- Select the Injection Token Type to be InjectionToken
- Under Class Name replace the word Client with Service
Finally, locate and click on the Generate Outputs button to get the actual service code generated.
Once done, copy the code and go back to the Angular app inside Visual Studio Code.
Step 8
Locate the movie-tracker
module folder and create a subfolder services
. Inside this new folder, create the movie-tracker.service.ts
file and paste in the copied code from NSwagStudio.
NSwag reads the ASP.NET Core Web API Swagger documentation, infers all the possible HTTP Responses that each Endpoint action might return (based on the attribute we've added) and generates a full Angular service for each and every Endpoint action. In addition, it generates a Typescript model class for the Movie model.
I've amended two things on the generated service.
First, I've added the following decorator to inject the service at the root level of the app:
@Injectable({
providedIn: 'root'
})
Second, I've replaced the checking for Status Coed 200 by Status Code 204 inside the processDelete method that runs upon receiving a response from the server after deleting a Movie.
That's all! You have a fully functioning Angular service that you can start using right away.
Step 9
Let's make use of the proxying support in the webpack dev server used inside the Angular CLI to divert calls for http://localhost:4200/api/
to the server running on https://localhost:44342/api
.
Using a proxy file replaces the need to enable CORS inside your ASP.NET Core Web API.
Create a
proxy.config.js
file inside thesrc\
root folderAdd the following inside the file
module.exports = {
'/api': {
target: 'https://localhost:44342/',
secure: false
}
};
- Go to the
package.json
file and amend thestart
NPM script as follows:
"start": "ng serve --proxy-config proxy.conf.js",
Serving the application now will take into consideration the proxy settings and does the redirection automatically for you.
You may read more about Proxying a Backend Server on the Angular website.
Step 10
Create the movie-tracker-grid
component to render the movies data in a table.
Run the following command to create a new component:
ng g component movie-tracker/components/movie-tracker-grid --module=movie-tracker --skipTests=true --inlineTemplate=true --inlineStyle=true
The command creates the MovieTrackerGridComponent inside the movie-tracker/components
folder.
Paste in the following HTML Markup and code:
The component renders a table using the Angular Material Table module. The component defines the columns to show inside the table: Title, WatchOn, Genre and Rating. In addition, it defines the Action column that contains the Edit and Delete buttons.
Right before rendering the table, we add a button to allow the user to create a new Movie.
The component accepts as input the Movies' data to display.
Also, the component defines an output of type EventEmitter
that emits actions like Edit
, Delete
and Create
.
Eventually, when we run the application, the table would look like this:
Step 11
Let's create the Search Bar component that allows us to search and filter the Movies.
Run the following command to create a new component:
ng g component movie-tracker/components/movie-tracker-search-bar --module=movie-tracker --skipTests=true --inlineTemplate=true --inlineStyle=true
The command above creates the MovieTrackerSearchBarComponent inside the movie-tracker/components
folder.
Paste in the following HTML Markup and code:
The component is straightforward. It defines an Input control that allows the user to filter the Movies data.
Inside the ngOnInit()
method it creates an Observable that wraps the Input's keyup
event and emits as output the text the user has typed.
Step 12
Let's now combine both the movie-tracker-grid
and movie-tracker-search-bar
components into the movie-tracker-list
component to have a fully functional and useful component.
Run the following command to create a new component:
ng g component movie-tracker/containers/movie-tracker-list --module=movie-tracker --skipTests=true --inlineTemplate=true --inlineStyle=true
This command creates the MovieTrackerListComponent inside the movie-tracker/containers
folder.
Paste in the following HTML Markup and code:
The component injects in its constructor the MovieTrackerService
. Inside the ngOnInit()
method it calls the movieService.searchMovies()
method with an empty string to retrieve all data stored in the database.
Whenever the user types any text in the search box the doAction()
method is triggered. The MovieTrackerSearchBarComponent emits an action of type search
. The doAction()
method handles the search
event type and emits the text typed which in turn causes the movieService.searchMovies()
method to be triggered again with a new search term.
Run the application by using the command yarn run start
and let's check the UI we have so far.
If you search the word fantasy
, the results shown, will be Movies whose title or genre fields contain that word.
Step 13
Let's handle creating, editing and deleting a Movie. We will be using the Angular Material Dialog module to open a popup Modal window to allow the user to do so.
Create the MovieDialogBoxComponent
by running the following command:
ng g component movie-tracker/components/movie-dialog-box --module=movie-tracker --skipTests=true --inlineStyle=true
The command creates the MovieDialogBoxComponent inside the movie-tracker/components
folder.
Paste in the HTML markup inside the movie-dialog-box.component.html
file:
The HTML markup displays a simple form to allow the user to create or edit a Movie based on the action passed to this component as we will see soon. In addition, we make use of a rating component to allow the user to rate a movie (you can check the code for details of the rating component).
Let's switch back to the movie-dialog-box.component.ts
file and paste the following code:
For an in-depth look at Angular Material Dialog module you can read the official documentation at Angular Material Dialog Module
The component receives, via its constructor, a data object that defines a movie property (an instance of a movie in the case of editing a movie or an empty object when creating a new movie). In addition, the data object defines the action property to distinguish the operation of creating, editing or deleting a movie.
On submitting the form, the Dialog passes back to the caller the action (create, delete or edit) together with the movie instance.
Step 14
Let's switch back to the movie-tracker-list
component and handle the edit
, create
and delete
actions.
Paste the following code inside the movie-tracker-list
component:
Clicking the Edit button triggers the action edit
. This action is handled inside the component that opens the movie-dialog-box
component to let the user edit the movie:
Clicking the Add Movie button triggers the action create
. This action is handled inside the component that opens the movie-dialog-box
component to let the user create a new movie:
Clicking the Delete button triggers the action delete
. This action is handled inside the component that opens the movie-dialog-box
component to allow the user to delete an existing movie:
Right after the movie-dialog-box
component dialog closes, the table is refreshed to get the latest data from the backend server.
Conclusion
We've seen how easy it is to connect an Angular app with an ASP.NET Core Web API app.
In the next article, I will add support for authentication using JSON Web Token (JWT).
This post was written by Bilal Haidar, a mentor with This Dot.
You can follow him on Twitter at @bhaidar.
Need JavaScript consulting, mentoring, or training help? Check out our list of services at This Dot Labs.
Top comments (2)
I'm trying to follow along with the tutorial step-by-step, but there are a few problems. The initial migration can't be created for the API. The error message is:
Unable to create an object of type 'MovieTrackerContext'. For the different patterns supported at design time, see go.microsoft.com/fwlink/?linkid=85...
I'm assuming that this is because the tutorial doesn't contain any instructions for setting up a connection string (or even installing/configuring the DB environment).
Also, the URL for the Github repo is invalid.
Hi Dan,
My bad! I kept the repo private on GitHub. Now it's public and accessible.
Can you share with me more log errors for the context issue you are facing?
As for connection string, it is mentioned in the article that I am using an SQLite and the connection string is simply the name of the database file stored locally.
Let me know if you have any other issues or concerns.