11.1: Configuring for Deployment
Last updated
Last updated
Right now our projects are running separately. We run the API from the .API
project using dotnet watch run
and we run the Angular application from the SPA
project with ng serve
.
When using this command, Angular transpiles our TypeScript
files and bundles them together. However, the bundles are only held in memory. They're also much larger than ideal.
Make note of the file sizes when running ng serve
(particularly the vendor.bundle.js
file):
Deployment can be tricky. There can be a number of things that can (and will!) go wrong. Every possibility cannot be covered here as many problems can result from individual machines, configurations, and too many variables to account for.
Don't get frustrated if you run into an error! Read through the messages and google and stackoverflow are your friend!
This will be good practice for solving problems without a step-by-step guide to follow.
Instead of using the development server - we can run the ng build
command and the Angular CLI will generate the files for us, instead of holding them in memory.
We can also add configuration to this in the .angular-cli.json
file located in the /src/
directory of your SPA
project.
By default, the output directory outDir
is set to a folder called dist
:
Running the ng build
command with the default configuration will generate a folder called dist
at the root level of the SPA
project. That could work depending on how you're planning to deploy.
We're going to configure the output directory to be the wwwwroot
folder in our .API
. This is a folder mentioned briefly at the beginning of the tutorial. It's for holding static content like HTML
, CSS
, and Javascript
. It's currently empty.
Update angular-cli.json
and change the outDir
value to the relative path to the wwwroot
folder in your .API
project:
Run ng build
.
After it has finished running - take a look at the new files generated in the .API/wwwroot
folder:
This is still a development build. The files are larger and not as optimized (through minification, tree shaking, etc.) as they will be for deployment.
Our .API
isn't serving any static files and it is unaware that we want it to. We need to do a little bit of configuration to allow it to serve the Angular static files. We'll do this in our middleware pipeline.
In the Configure()
method of the Startup.cs
class - add two methods:
app.UseDefaultFiles();
- this tells the Kestrel server ASP.NET uses to use the default file names - so, it will know to look for a file called index.html
.
app.UseStateicFiles();
- as you could guess - this is to configure the application to serve static files.
Now, we should be able to run dotnet watch run
and navigate to port 5000 instead of port 4200 to access our Angular application!
However, what happens if you login and refresh the page?
Ugh.
Why is this?
ASP.NET Core thinks this is working like a Razor
front end. It sees we're trying to navigate to /members
- and thinks there should be a MembersController
with an Index()
method to render the view. It can't find it, so it 404
s.
Fortunately, ASP.NET Core comes with a handy solution to this problem. We can tell our application - that if it encounters any route to hand over routing to Angular.
Again, in the Configure()
method of the Startup.cs
class add the following to the existing app.UseMvc()
method:
Now, we need to create the Controller that we specified in the above configuration.
In the Controllers
folder, add a new class called FallbackController
. We'll add a single Index()
method that returns a physical file - our index.html
file in the wwwroot
folder:
Now our application should be working as expected from port 5000.
We're still currently serving a development Angular application. Now, we're going to run a production build.
We need to do a bit more configuration in our Angular application before, though.
First, we need to add the apiUrl
to the enviroment.prod.ts
file.
We also need to change the app.module.ts
file because we can't use a function inside our imports array for a production build. Currently, we have one where we're importing the JwtModule
.
At the top of the file, under the import statements and above @NgModule
, add the following:
Now, in the imports array - we can use this jwtConfig
variable instead of passing in a function:
Now we'll try running a production build. This is a place where things can get tricky. The TypeScript
and Angular
compiler will be much more strict now. Things it let us do before without an error, much less breaking our application, can break our application now.
In your terminal, run ng build --prod
.
Hopefully it compiles without any errors. Again, if you get errors - read it carefully, think about what may be causing it, and google for solutions. Don't panic - it may just be a few small changes like changing a private member to public.
Once successful - notice the much smaller bundle sizes compared to our development build:
Restart your .API
project - and, verify that the app is still working using our production Angular files.