I created this post to get some insights from the community.
A little while ago with the release of .NET Core 3.0 the usage of the well-known and widely used spa.UseSpaPrerendering
has been marked as Obsolete.
Around early-2019 I implemented SSR using .NET Core in a project that uses Angular but needed SEO and better loading perf.
1 year later (now, beginning of 2020) they want the same for a different project. But it already uses Core 3.1. immediately we noticed the Depricated flag, so I went searching for a way to do it ourselves.
From past experience the SSR problem had 2 parts, the first being getting your Angular app to actually be able to run in Server-side. So getting rid of or working around all the stuff that is unable to be executed in Server-side (working around usage of window API’s, by using isPlatform stuff in Angular). Second part was to actually get .NET Core to spin up the Angular CLI to start the actual pre-rendering. This was done using the UseSpaPrerendering
.
Analysing the documentation that told us to figure it out ourselves and checking out my code from the past, things actually started to make sense.
I looked at the commands in my package.json file the 2 main commands executed were build:ssr
to actually pre-compile the whole server/main.js next to the browser/… files. The second command was the serve:ssr
, which was going to be executed by the UseSpaPrerendering
code (at least that is what I assume).
In practice, our CI/CD would go and execute the build:ssr
and publish all the files to the App server running .NET Core runtime. and using the UseSpaPrerendering
code it would then execute the serve:ssr
.
Now jumping forward towards the present where I need to find a solution. I figured that I could also just run the necessary commands myself. So after excluding some of the non-SSR compatible code in my Angular I ran the build:ssr
command myself, followed by the serve:ssr
command. which worked, my Angular app was SSR rendered, by served by node itself rather than by .NET Core.
Next step was that I tried to that in my .NET Core Startup file. for now I did the build:ssr
myself (because in production it would be done by CI/CD) and I re-wrote the start
script in my package.json to run the command npm run serve:ssr
. I started that command using the spa.UseAngularCliServer(npmScript: "start");
code in my startup.cs and there I had it, my .NET Core runtime starts up both my API and my SSR Angular app.
So far so good, but only 1 problem now. my SSR Angular is hosted on port 4000 default and also listens to that port (I can see that in my output) and my API listens on port 5000(http) and 5001(https).
So now I have a couple of questions about this:
- Is this the correct way of doing this now?
- How can I make sure that in production, when one goes to my app, that the node listener will kick in?
- Would it be better if I just completely separated my .NET Core API and Angular SSR app completely ? And Thus also deploy them separately?
2
Answers
I’m struggling with netcore 3.1 and angular too when it comes to deploy the project on azure or anything.. did you find anything? Could you provide your startup file?
when I use dotnet publish, package.json is not copied to publish/ClientApp directory so the command used by
spa.UseAngularCliServer()
fails or it just doesn’t find /index.html.for now, I run my project locally like this:
To anyone facing this issues, I’ve just solved it and here is our solutions but there are few facts:
Consider that during deployment you will have to generate Web.config according to this new structure
-Handler iisnode -NodeStartFile dist/server/main.js -appType node
[server.ts] – Having that in mind consider also to set the browser path according to your runtime environment so that if you are in production it should be ../browser
[server.ts] – Order matters in server.ts. IF YOU FACE BROWSER API ISSUES it is because “import { AppServerModule } from ‘./main.server‘;” MUST be placed AFTER domino declarations.
Here is a working example on a server.ts that is also using i18n redirections according to url requests with a locale string (now that I solved this i18n issues too it I can tell you that it worth to read the docs).
I still need to work a bit on this code and in our app (SSR and oauth issues, another funny topic) but I want to share it because it took us almost 20 deployments to fix these issues.
Final words: if you come here after an angular 8 migration I’ll be glad to help you and give you nice hints but, honestly, follow the guide and read carefully the docs. Also, if you are using Azure DevOps pipelines, you should consider using an npm cache. Our as is large and we are now saving more than 12 minutes on each build process (That is a huge amount of time, isn’t it?) Feel free to get in touch with me.
Juan