Seamless Deployment Excellence: Orchestrating Your Angular App with ASP.NET Core and Azure SQL
In this article, I will show you a way to quickly deploy your full-stack application to Azure using Angular and ASP.NET Core 6.
Essentially, while you can use your own hosting provider to deploy your projects, deploying multiple applications consisting of Angular and ASP.NET Core on the same host might lead to CORS policy issues. CORS problems can be tricky if you don’t identify the correct points. That’s why using Azure to deploy your application can be more cost-effective and, in some cases, even free.
Azure is easier to configure in terms of CORS as it comes with its own CORS configuration.
Create an Angular project using
ng new <project-name>
After creating your Angular app, open it using VS Code.
<your application root>/code .
You should see a similar screen after completing all the processes.
Now, it’s time to create an ASP.NET Core 6 web application to set up some RESTful APIs.
You should see a screen like this after completing all the processes.
Now, it’s time to create a SQL database on Azure. Go to the
Click on “Sign In” and enter your credentials.
Before you create an SQL database, you need to set up a subscription. Go to the search box and type “subscription,” then click on “Subscription.”
Click on the “Add” button and fill in the details.
After filling in the details, click on the “Review + create” button, and then create it. Now, you have a subscription name and can use it to create an SQL database. Go to the search box, type “SQL database,” and select “SQL databases.”
Click on “SQL databases.”
Click on the “Create” link.
Select Subscription and Resource group. If you do not have a resource group, click on the “Create new” button and create a new resource group.
Type a database name that you like, and if you do not have a server, click on the “Create a new” link.
You will see a page like this; enter the details and then click on the “OK” button.
We do not need SQL elastic pool at this time, which is why keep it as “No.” The workload environment will be set to “Production” as it is.
Now, you should configure your database; otherwise, you might face a big bill at the end of the month. Go to “Configure database” and then select “Basic” service tier. The estimated cost should be 4.90 USD.
You do not need backup storage right now, and that is why the third option will be okay for you. Click on the “Review + create” button and continue.
You will see a summary page of your SQL database; then, click on the “Create” button if you are comfortable with the summarized page.
After the SQL database is created, you should be able to see the new SQL database in the list. Click on the database name, and then check the details of the SQL database.
In order to connect to the SQL DB on Azure, you have to create a firewall rule for the Backend. That is why go to your backend app and copy the IP.
Create a firewall rule, stating that your backend IP can access the SQL Database.
Now, it’s time to configure our backend project. Click on the “Show database connection string” link, and then copy the connection string.
Open the backend project, go to the appsettings.Development.json
file, and enter the connection string.
To test your database connection, go to Microsoft SQL Management Studio and attempt to connect to the server.
If you encounter the error below after clicking on the “Connect” button
Go to Azure SQL DB and click on the “Set Server firewall” tab.
Set the Public network access to “Selected networks” and then click on the “Save” button.
Now, you should be able to connect to the database. If Management Studio prompts you to sign in to Azure, just enter your credentials and connect to Azure.
After connecting to the Management Studio, you should be able to see your database in the list of databases.
In order to test our client and backend API, we should create a table. The code below demonstrates how to create a table using T-SQL. If you prefer not to use the code, you can see how we create a table using EF Core below.
create table [Users]
(
Id int primary key identity(1,1),
[Name] varchar(25),
LastName varchar(25)
)
Now, it’s time to create a DbContext to connect to the Database. To accomplish this, follow the steps in Visual Studio:
- Add Entity Framework packages.
- Create a “data” folder.
- Inside the “data” folder, create an “entity” folder.
- In the “entity” folder, create a user entity model class.
- Create a DbContext class in the “data” folder and implement DbContext.
Add Packages…
In order to use Entity Framework and connect to the database, we have to install the following packages:
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.Relational
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
Right-click on the solution and select “Manage NuGet Packages.”
After adding the packages, create a folder named “Data.” Inside the “Data” folder, create another folder named “Entity.”
Create a “UserEntity” class in the “Entity” folder and a “DbContext” class in the “Data” folder.
You can create your folder and class structure however you want; you don’t have to follow my structure.
After all processes, you should have folders and classes like the ones below:
User Entity Class
namespace AzureDeployment.Data.Entity;
public class UserEntity
{
public int Id { get; set; }
public string? Name { get; set; }
public string? LastName { get; set; }
}
DbContext class
using AzureDeployment.Data.Entity;
using Microsoft.EntityFrameworkCore;
namespace AzureDeployment.Data;
public class MediumDbContext : DbContext
{
public MediumDbContext()
{
}
public MediumDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<UserEntity> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer("<your connectionstring>");
}
To create a database using the code
add-migration <migration-name>
If you encounter an error, place this code in the DbContext class.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("<your connectionstring>");
}
}
After the migration file is created, use the code
update-database
After all processes, you should be able to see the database with a table
Use the SQL code to create a row
INSERT INTO [Users] VALUES('Name','LastName')
Now, create an API to fetch the data. To do this, create a folder named Services and implement a fetching service in it. Afterward, inject the service into the controller and configure it in the program.cs using the dependency injection technique.
After creating folders and files, your structure should look like the one below.
IUserService.cs
namespace AzureDeployment.Services;
public interface IUserService
{
Task<object> GetAllUsers();
}
UserService.cs
using AzureDeployment.Data;
using AzureDeployment.Data.Entity;
using Microsoft.EntityFrameworkCore;
namespace AzureDeployment.Services;
public class UserService : IUserService
{
private readonly MediumDbContext _context;
public UserService(MediumDbContext context)
{
this._context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task<object> GetAllUsers()
{
try
{
return await _context.Users.ToListAsync();
}
catch (Exception excp)
{
// if you have a logging mechanism, implement here
throw;
}
}
}
UserController.cs
using AzureDeployment.Services;
using Microsoft.AspNetCore.Mvc;
namespace AzureDeployment.Controllers;
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
this._userService = userService;
}
[HttpGet]
[Route("GetAllUsers")]
public async Task<IActionResult> GetAllUsers()
{
return Ok(await _userService.GetAllUsers());
}
}
After creating the API, test it to verify if it is working or not. Run the project and check it on Swagger.
You should have a result similar to the one below.
Now, it’s time to configure the client side. Create a service to retrieve data from the API. To do this, navigate to the Angular project and create a service using
ng generate service <your-service-name>
Here, you have to import HttpClientModule into the imports section of the app.module.ts file
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
After importing HttpClientModule, you will be able to inject HttpClient into your service and create the function to retrieve all user data from the API. Before creating the function, go to the environment.ts file under the environment folder and add the backend URL.
Go to your service and create an instance of it after adding the backend endpoint to environment.ts
Create the HTTP GET method and retrieve the data from the API in User.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class UserService {
private API_URL=environment.API_URL;
constructor(private _httpClient:HttpClient) { }
getAllUsers():Observable<any>{
return this._httpClient.get<any>(this.API_URL+'api/user/GetAllUsers');
}
}
In order to use Observable, import RxJS because Observable is coming from the RxJS library.
Implement the getAllUsers function and retrieve the data from the service in app.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'Test';
users:any=[];
constructor(private _userService: UserService) { }
ngOnInit() {
this.getAllUsers();
}
getAllUsers() {
this._userService.getAllUsers().subscribe({
next: response => {
this.users=response;
},
error: error => alert('Error occured when retrieving the data')
})
}
}
Use the ngFor directive to iterate through the data and display it in app.component.html
<ul>
<li *ngFor="let user of users">
{{user.name}} - {{user.lastName}}
</li>
</ul>
The last step is to enable CORS to facilitate communication between your Angular App and API. To do this, go to your backend project and update the program.cs file. (If you do not enable it, you will encounter a CORS error when attempting to retrieve data from the API.)
To enable CORS
var confCors = "ConfCors";
builder.Services.AddCors(options =>
{
options.AddPolicy(confCors,
policy =>
{
policy.AllowAnyHeader().
AllowAnyMethod().
AllowAnyOrigin();
});
});
.....
.....
.....
.....
app.UseCors(confCors);
You can specify a particular client endpoint if needed, but in this example, we will allow all endpoints.
After enabling CORS, your program.cs should resemble the one below.
using AzureDeployment.Data;
using AzureDeployment.Services;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddControllers();
builder.Services.AddDbContext<MediumDbContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("AzureDbConnection"));
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var confCors = "ConfCors";
builder.Services.AddCors(options =>
{
options.AddPolicy(confCors,
policy =>
{
policy.AllowAnyHeader().
AllowAnyMethod().
AllowAnyOrigin();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(confCors);
app.UseAuthorization();
app.MapControllers();
app.Run();
Now, you can test your projects. First, run the backend project, and then run the Angular app. For the Angular app, you can use
ng serve --o
After running both projects, you should be able to see the desired result
Now, it’s time to deploy our projects to Azure. To do this, create a Web App. Go to Azure and type “App Services” in the search box
Click on “App Services” and then click the “Create” button
Fill in the necessary details
I chose Free F1 because of this article. You can choose according to your requirements.
Click on Review+Create after filling in all the details and check your web app details. Click on the Create button if you are okay with your web app details.
This will be for the backend, and now you will deploy your backend to the web app. Click on Go to resource after deployment is done, and then click on Get publish profile to download the profile.
Go to your backend project, right-click on the solution, and select “Publish.”
Select “Import Profile” and add the import file that you downloaded from Azure. Click on “Finish.”
Click on “Publish” and wait for the process to be completed.
You should receive a “succeeded” message once it is done.
Let’s try to call the API from the client side. First, you need to update the environment.ts file because you will try to connect to Azure. Go to your MediumBackend web app, copy the URL, and paste it into the environment.ts file.
The ‘environment.ts’ should be as follow
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
API_URL:"https://mediumbackend.azurewebsites.net/"
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
Once you try to fetch data, you may encounter a CORS error.
Because Azure has its own CORS policy configuration, you need to configure it before calling the API. To do this, go to Azure > your backend project and then search for CORS.
Add http://localhost:4200 (or your UI endpoint) to Allowed Origins and click on the Save button.
After the configuration is complete, you should be able to see the data on the page.
Now, you have to create one more Web App for the Angular project. You can refer to the instructions on how to create a Web App above. Ensure that you select the Node.js version compatible with your Angular app. In my case, I am using Node.js v16, which is why I selected it.
Note that if you do not select a Windows machine, you might encounter errors when trying to deploy your Angular app.
Could not detect any platform in the source directory.
Error: Couldn’t detect a version for the platform ‘nodejs’ in the repository.
Once you create a web app, go to VS Code.
- Update
environment.prod.ts
.
export const environment = {
production: true,
API_URL:"https://mediumbackend.azurewebsites.net/"
};
2. Execute ng serve --configuration=production
; after that, you should have a dist
folder.
3. Install the Azure App Service extension.
Once you install the extension, connect to your Azure Web App.
Right-click on your Angular service and select “Deploy to Web App.”
And then select “Browse” and choose your dist
folder.
After deployment is done, you might encounter the error below.
You need to make some configurations:
- Configure your root.
- Update CORS.
Go to the configuration of the Angular App and set index.html
as the default.
Go to “Path mappings” and update your physical path. You can find your application name in the package.json
.
Go to CORS and enter your Angular app endpoint in the backend app service.
After the configurations are complete, you should be able to see the result.
It is a lengthy explanation, but I aimed to illustrate how a full-stack project can be deployed to Azure easily. You can access the code through the links below.