Coming from an ASP.NET background, developers are familiar with the File, New Project… experience. This is the process to setup a new project in Visual Studio – typically from a template to help you get started effectively.
These starter projects include everything in the box. They have UI rendering, authentication, sample data APIs, and more. And while they are great for getting started, they may not be the best design for the application you are building.
When it comes to building an Angular application, how should it integrate with the server-side application if at all? What design options are available to you and how do you know which is the right one? This is an often-overlooked topic in Angular-focused material. One the other end, server-side articles tend to encourage using server-side features without considering that you are using a very powerful front-end framework already.
Level 0 – No Server Application
This level includes features that are configurable to have no server-side dependency. This means that when the application runs, Angular handles these features without requiring the server to process any of the logic. This configuration may be desirable especially when static file hosting is the preferred deployment option.
Even though there is no run-time server dependency, you can still leverage the power of a build to optimize your application. These are some of the features you can configure with no server-side backend.
The Angular router is optionally configured to use the hash URL style. In this configuration, each route is represented in the URL following the hash symbol, like
example.com/#/products/1. The main benefit is that when someone enters this URL into the browser, they are going to the same endpoint each time,
example.com. When the application loads, the router kicks in and navigates to the correct view.
Angular includes utilities to internationalize applications. There are several phases to this process which you can read about in the documentation. At the end, you are left with multiple versions of your application that are deployed to their own directory as static files. At this point, there are several options for routing the user to the correct version of the application. The choice can be left to the user in the UI or the application can read the language settings in the browser to automatically route to the correct locale.
Level 1 – Host Application Integration
This level consists of functionality where the host application and the Angular application work together to produce the runtime functionality. While the server may not know intimately about the Angular application, both the server and client must be configured in a complimentary manner and make assumptions about the other's behavior.
As opposed to the hash style URL discussed earlier, the more common HTML 5 pushState style URL depends on server support. These URLs lack the hash symbol, for example
example.com/products/1. The issue here is that when someone navigates to your application the first time using one of these URLs, the Angular application doesn't have a chance to handle the route. Instead, the server receives the request first and then returns the Angular application to the client to then complete the routing process. You can see an example with ASP.NET Core here.
Sometimes, there are reasons to lock down your Angular application to only authorized users. While the Angular application could handle this based off of Web API authentication, you also have the option to authorize with the host application. For instance, the first time someone navigates to your web application, the server can return the 'not authorized' messaging without returning any of the Angular code to the browser.
As mentioned previously, Angular's rendering is more than capable of generating your application's UI. Generally, this rendering occurs in the browser. However, applications optimized for SEO and time-to-load performance, benefit from server-side rendering.
This is where Angular Universal fits in. Instead of using the server framework's view engine, Angular Universal renders Angular templates server-side. The browser receives the HTML and CSS to display the page immediately without client-side rendering. Subsequent views are loaded via AJAX and rendered on the client to reduce subsequent payload sizes. The framework supports both Node.js and ASP.NET Core backends.
Using Angular Universal does increase complexity and imposes certain restrictions in how you write your Angular application. Be sure that if you need this extra performance boost that you understand the trade-offs. Read more about it on GitHub.
Internationalization is an area where the host application may not have a direct role but may need to assist integrated routing, authentication, and/or rendering to provide the desired experience.
Logging also doesn't necessarily represent a tight integration between the host application and the Angular application. However, consider that as you increase the server's role with routing, authentication, and/or rendering, you should consider logging any errors resulting from this increased responsibility.
Level 2 – Web APIs
These dependencies are server-side dependencies. However, they do not have to live within the same web application hosting the Angular application. By keeping these dependencies in their own code base, there are many advantages. Builds and deployments are performed only for the applications that have changed. The APIs are built with whichever technology the team and/or company decide so long as it works over a common protocol – typically HTTPS. If you are familiar with the concepts of Web APIs and/or Microservices, this group encompasses those concepts.
Some examples of web API responsibilities that an application requires include data access, logging, and usage analytics. Interestingly, none of these concerns require any knowledge of the Angular application. In fact, they could serve many different front-end applications.
Authentication at this level is typically handled directly between the Angular application client and the Web API server. There are many ways to do this but today's solutions generally use some form of JSON web tokens to maintain the user's identity.
At this level, any assets requiring i18n would be handled by the web API using server-side techniques. Again, this has no coupling to the Angular application beyond the API contract.
A Case for Bundling
Sometimes, applications are small and the API that they access is small. In this case, you may decide to keep this functionality together in the same application. They share the same build and the same deployment. Sometimes the effort to split these functions isn't greater than the payoff.
In this case, consider your development and design carefully as to avoid creating unnecessary coupling between the concerns. The web API may one day 'grow up' to need its own project so develop accordingly.
Every application is different. Hopefully by reading about how application concerns create dependencies between the server and the Angular application, you can apply these considerations to your own architecture.
One thing you might have noticed, there's no discussion of development tools that integrate with the server. This is a topic for a future post so stay tuned.
Ultimately, there is no one way to design an application. However, by knowing your options, you're better equipped to make lasting design decisions for your project.
What do you think? Is there functionality you prefer to run on the server? The client? Please share in the comments.