Angular2 and Spring Boot. How to serve front-end? - node.js

I have Spring Boot as back-end and Angular2 as front-end. I want to develop both of them separately and deploy onto Heroku.
They shouldn't have any common dependencies and should be in separate git-repos.
As I understand, there are two main ways to implement:
run npm build and copy dist folder into resource folder of Spring application so last will handle it as a static content
run server for serving exclusively Angular app which will communicate with Spring app (CORS problem appears here?) so there are two servers at sum
I think first way is a bit "dirty" since I do not think that copy folder from one project to another is any good.
And second way is overkill because I have two servers (Tomcat and Node.js, for example). Why should I have server with Angular app if I can simply put Angular inside Spring?
Is there is any more rightful way to make aforementioned?
Thanks.

In my organization, we have a lot of Spring Boot and Angular apps. When two servers are unnecessary, Spring Boot can serve up the static content from any supported URL (such as "http:" or "file:"). Simply pass this argument to your app on startup:
--spring.resources.static-locations=<url>
Spring Boot can also support Angular single-page app routing by including the following web MVC configuration. This ensures that even if the user refreshes the page in the browser, Spring Boot will still serve up the contents of index.html for other angular routes.
public class SinglePageAppWebMvcConfigurer extends WebMvcConfigurerAdapter
{
#Autowired
private ResourceProperties resourceProperties;
private String apiPath = "/api";
public SinglePageAppWebMvcConfigurer()
{
}
public SinglePageAppWebMvcConfigurer(String apiPath)
{
this.apiPath = apiPath;
}
protected String getApiPath()
{
return apiPath;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations(resourceProperties.getStaticLocations())
.setCachePeriod(resourceProperties.getCachePeriod()).resourceChain(true)
.addResolver(new SinglePageAppResourceResolver());
}
private class SinglePageAppResourceResolver extends PathResourceResolver
{
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException
{
Resource resource = location.createRelative(resourcePath);
if (resource.exists() && resource.isReadable()) {
return resource;
} else if (getApiPath() != null && ("/" + resourcePath).startsWith(getApiPath())) {
return null;
} else {
LoggerFactory.getLogger(getClass()).info("Routing /" + resourcePath + " to /index.html");
resource = location.createRelative("index.html");
if (resource.exists() && resource.isReadable()) {
return resource;
} else {
return null;
}
}
}
}
}

Option 1
One server process hosting REST APIs and another server process hosting Angular UI
This option is the recommended option in a MicroServices based architecture where the individual APIs (or small related group of APIs) are run and scaled separately by hosting them in separate server processes. In such scenarios, if the Angular UI is also bundled with the REST APIs, it would mean that every bug-fix or enhancement in the Angular UI would require rebuilding and redeployment of your services. And when we start doing that, it would defeat the purpose of Microservices based architecture.
Therefore, in such an architecture one or more server instances would host only the Angular UI. The Angular UI would in turn call the individual APIs through an API Gateway using some service discovery mechanism. However, Microservices based architecture is not trivial - it's complex with a lot of moving parts. Such level of complexity can be justified for large projects.
Option 2
One server process hosting both REST APIs and Angular UI
This option is the recommended option for small to medium sized projects where the user base is a few hundred users.
In such projects, create a single repository where a Maven project would contain two sub-modules - one for the REST APIs and the other for the Angular UI.
Use the Maven plugin "frontend-maven-plugin" to build the UI part. This will automatically download the specified NodeJs version and invoke appropriate npm commands to build the Angular project. Finally, using the Maven element copy the Angular dist folder to the Spring Boot static folder under the resources folder. (In the .gitignore file exclude the static folder so that the Angular distribution files are not checked into the source control along with the REST APIs).
Now, as the Java build will start, it would automatically include the static folder in the fat jar which would now serve both the APIs and the Angular UI.

So far I created applications with angular and spring-boot, using one git repository, but two different maven projects, one for the backend, one for the frontend.
With Maven than I built one fat jar with an embedded Tomcat and deployed it to Amazon EC2.
I also experimented with Heroku and you could for sure deploy the same fat jar there.
For the next project I would take another approach and deploy all static resources like html, javascript etc. to Amazon S3 and only the spring-boot app to a provider like heroku.
Frontend deployment this way seems to be much easier, faster and cheaper.
There is also a blog post about Using AWS S3 to Store Static Assets and File Uploads

Related

how to set dynamic enviroment variable for static nextjs app deployed on IIS

I'm trying to deploy the static nextjs app(next export) on local Windows IIS.
The web app communicates RestFul API server deployed on the same network.
The problem is the address of RestFul API server.
It's from .env.* file. The env file has the key-value pair of (NEXT_PUBLIC_API_URL, http://192.168.1.12:8080/api).
When build the app with next build command, the value(http://192.168.1.12:8080) is inserted to output source code directly.
It's ok in development process because the address of API is fixed, but it must be changed dynamically after deployed on production.
Is there anything I can do to export values for static nextjs app dynamically on local IIS?
To solve this problem I created some static-env.json file in public folder like below:
{
"apiURL" : "http://192.168.1.12:8080/api"
}
and import it by import staticEnv from "/public/static-env.json, but the result is the same with above. the value itself is inserted to code directly.
I think if this "inserting" action can be prevented, I can use this method.

How to document models & services which are in a separate project

I'm looking to demonstrate ServiceStack to my team, and as part of the demo I want to create some documentation. The UseCases.SwaggerHelloWorld runs fine, but my project (which has 1 functioning service) returns an empty swagger page. When I visit /resources it returns:
{
"swaggerVersion":"1.2",
"apis":[],
"apiVersion":"1.0",
"basePath":"http://localhost:29672",
"info":{"title":"Reports.Api"}
}
I then copied the Hello* service and models into my API project (in the same assembly as the host) and this worked. I then moved the service and models out into separate projects and the docs disappeared again.
So my question is, how can you document APIs when the models are in a separate DLL? The project structure is the same as recommended when you create a solution using the servicestack solution template.
The AppHost constructor tells ServiceStack which assemblies it should scan to locate, register and auto-wire your Services, e.g:
public class AppHost : AppHostBase
{
public AppHost() : base("Hello App", typeof(HelloService).Assembly) {}
}
It supports specifying multiple Assemblies if your Services are maintained in different projects, e.g:
public class AppHost : AppHostBase
{
public AppHost() : base("Hello App",
typeof(HelloService).Assembly, typeof(OtherService).Assembly) {}
}
The Modularizing Services docs has more info.
The DTO's used by the Service can be in a different project, you just need to specify which assemblies the Services are in. Although it's recommended the DTO's are maintained in a separate dep-free project which is the recommended Physical Project Structure that's contained in each ServiceStackVS VS.NET Project Template.

Serving static resources from external directories

What should be the correct way to serve images, or any other static resources like css and javascript, from a directory existing outside of the application?
I used to achieve this very easily in Spring MVC applications by using the mvc:resources element in xml configuration, or by extending WebMvcConfigurerAdapter and adding the respective resource handlers in Java configuration, then specifying the file system path where the resources were stored.
Having in mind that, as far as I can tell, jhipster doesn't use Spring MVC how could I achieve this functionality?
I tried configuring Spring MVC in jhipster but introducing the dispatcherServlet only caused the application to break (as expected, right?), and I'm still getting the hang of Angular so I'm not sure if the solution is that way.
Thanks in advance.
UPDATE:
Added my solution below.
For dev just place the file in /src/main/webapp/yourdir/ and it should be served (assuming it's a public static resource). For production, you'd need to add it to the static filter in config/WebConfigurer.java:
/**
* Initializes the static resources production Filter.
*/
private void initStaticResourcesProductionFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering static resources production Filter");
FilterRegistration.Dynamic staticResourcesProductionFilter =
servletContext.addFilter("staticResourcesProductionFilter",
new StaticResourcesProductionFilter());
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/index.html");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/images/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/fonts/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/scripts/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/styles/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/views/*");
staticResourcesProductionFilter.setAsyncSupported(true);
}
Thanks to the magic of Spring Boot, now I know you can configure MVC related stuff by extending WebMvcConfigurerAdapter. So I created my own configuration class that overrides the corresponding method, in this case addResourceHandlers and it works like a charm.
Just note that the #EnableWebMvc is NOT needed, otherwise you will mess with JHipster front end.

How to modularize AngularJS applications / plugins

I have a couple of (software-)architecture questions regarding a migration from Grails (REST-API, parts of AngularJS, MongoDB, Tomcat, Spock, several plugins) to Node.js + Angular.js.
I probably have to explain the structure of the Grails project fist, so here we go:
There is a main Grails application (beside a couple of other applications), which is built on several plugins. Each of these plugins is able to get executed by itself - that means it has its own UI, individual templates, services, controllers, routes, tests etc. It is also hosted on different repositories.
This is done by the Grails plugin mechanisms. The benefits are less testing-efforts, less compiling time, modularization, single responsibilities and so on.
But still, the time to compile and test are too expensive. Also I don't like the fact that the API delivers parts of the templates/views. I would like to have the backend APIs "just to be backend APIs" and the frontend "just to be the frontend".
So each AngularJS application/plugin will provide its own view, routes, service etc. But they might also depend on other plugins.
So what I would like to achieve is as follow:
One main AngularJS application, which includes several plugins (a plugin can be something like a report-generator, a guestbook or whatsoever, speaking of a single independent part of an application, either with a specific route, or just a small part of the page).
Each plugin must be a stand-alone AngularJS application (probably executable during development via grunt or so). So that the UI developer does not need to start the whole backend application, further that we may run functional tests only with JavaScript
Communication only via REST, The frontend must retrieve all it's data from the APIs
Each plugin must be testable on its own
A Plugin might require other plugins to work
The main index.html (and app.js?) might be provided by a Nginx server, which is decoupled from the rest of the backend (API)
Though I have a specific picture in my head, I am struggling in how to setup this architecture.
In Grails the plugin mechanisms are somehow merging the plugin dependant settings (like URL mappings, dependencies, etc) to the main application in which they get included/injected - this is what I want to achieve with AngularJS as well. So:
Are there some kind of same mechanisms for AngularJS?
How may I provide/merge the routes of each plugin into the main application?
How can I declare application- and plugin-dependencies?
What tools might be usefull for the build process?
How to establish lazy-retrievments of the plugin-resources (css/less files, views, services etc)?
Prevent the application to provide all resources of the plugins on startup (I guess the routes are required on startup though)
Since this is not just a how-to-do-this-or-that question I excuse myself if I am missing important parts or if some parts are not clear enough. Just ask me and I will answer each question in depths.
** This answer is incomplete **
I want to make sure I understand you before I dig into this.
Here's a quick implementation of a Loader module to manage lazy loading (plugins, vendor assets, admin stuff etc).
Does this help?
angular.module('Bizcoin.loader')
.service('Loader', Loader);
function Loader($injector, $ocLazyLoad, User) {
var Loader = {
load: load,
get: get,
plugins: {},
flags: {}
};
init();
return Loader;
function init() {
load('vendors');
if (userIsAdmin)
load('admin');
}
function load(plugin) {
Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js');
return Loader.plugins[plugin].then(setFlag);
function setFlag() {
return Loader.flags[plugin] = true;
}
}
function get(plugin) {
return load(plugin).then(function() {
return $injector.get(plugin);
});
}
}
I work on an large .Net/AngularJS application that is composed of 20+, independent, Areas (or modules) and some core functionality common and reused across all Areas.
Let me go into detail on how I do this for my particular case, and it might give some ideas. The fact that I use .Net is irrelevant here, since this can achieve with any framework.
Each Area acts as an independent application that depends only on the core functionality, always present. Each Area has its own ASP.Net MVC route.
Each Area registers with the core application the menu links it wants to provide.
When the customer goes to the application dashboard, only the core part of of the application. When the user clicks on link in the menu, it will navigate to the content provided by one of the Areas, and only the core plus the assets of that Area are loaded.
Lets see how this is done.
In the main page of the application I load the scripts like this:
<script type="text/javascript">
// a JS object with all the necessary app data from the server.
// e.g.: menu data, etc
window.appContext = #Html.Action("ApplicationContext", "Portal"));
</script>
#Scripts.Render("~/bundles/angular-main")
#RenderSection("AngularAreas", required: false)
I make use of dot .Net bundles and sections.
The main (core) AngularJS part of the application consists of angular configuration, internationalization services, global notifications service, reusable UI components, etc. This is loaded is #Scripts.Render("~/bundles/angular-main").
The #RenderSection("AngularAreas", required: false) section will be filled in by each area when the user navigates to that Area.
Lets see some AngularJS code.
Here is part of the main app.ts.
// If user is visiting an Area, the NgModules array will be augmented.
// with the modules the Area wants to provide (to be bootstrapped)
export var LoadNgModules = [
NgModules.Config,
NgModules.Core
];
angular.module(NgModules.Bootstraper, LoadNgModules);
angular.element(document).ready(function () {
angular.bootstrap(document, [NgModules.Bootstraper]);
});
Lets look at an example Area now.
And here is how an Area would supply its assets, to be outputted in #RenderSection("AngularAreas", required: false):
#section AngularAreas {
#Scripts.Render("~/bundles/areas/staff-management")
}
Its a simple bundle containing all the scripts for that Area.
Now, let's see the important part of the AngularJS code for this Area.
var StaffManagementNgModule = 'areas.staff-management';
// Push our self into the modules to be bootstrapped
LoadNgModules.push(StaffManagementNgModule );
// Define the module
angular
.module(StaffManagementNgModule , ['ngRoute', NgModules.Core])
.config([
'$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => {
$routeProvider
.when(staff', { template: '<staff></staff>' })
.when('staff/details/:id', { template: '<staff-details></staff-details>' });
}
]);;
That is all, from here the Area is a normal Angular application.
To summarize, we load the main (core) AngularJS functionality and provide the LoadNgModules array that an area can fill with its own modules.
we load the Area scripts and and "our self" to the LoadNgModules array.
finally the run angular.bootstrap.
For completeness, here is a fragment of C# showing how an area would indicate to the main application that it is available
public class ItemManagementModuleRegistration : IModuleRegistration
{
public void Register(ModuleContext moduleContext)
{
string thisAreaName = "Staff";
moduleContext.RegisterMenu(menuContext =>
{
var ItemsMenu = menuContext.Items(thisAreaName);
// add urls and stuff...
});
// register more stuff with the moduleContext
}
}
Using reflection one can easily find what areas are "installed".
These are the main moving parts of the setup.
Each Area can have its own API and tests. It is quite flexible.

Bundling not happening when deployed to production server

I have created a bundle. However its not working when deployed to production server. I am running MVC4 RC
My bundle looks like
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/core").Include("~/Scripts/min/jquery-1.*",
"~/Scripts/min/core.js",
"~/Scripts/min/business-signup.js",
"~/Scripts/min/slider.js",
"~/Scripts/min/label-text.js",
"~/Scripts/min/consumer-account.js",
"~/Scripts/min/modernizr-2*"));
bundles.Add(new StyleBundle("~/Content/min/css").Include(
"~/Content/min/core.css",
"~/Content/min/layout.css",
"~/Content/min/reset.css",
"~/Content/min/sprite.css",
"~/Content/min/pricing.css",
"~/Content/min/homepage-slider.css",
"~/Content/min/transactions.css"));
}
From my layout I call the bundles like
#Styles.Render("~/Content/min/css","~/Content/min/css")
#Scripts.Render("~/bundles/core")
However there is no minifaction or bundling happening. Instead a single request is made for each of the items within the the bundles.
I know the server is working correctly as I have another site running on the same server and the bundling is working ok.
Can anyone see something obvious to what I am doing wrong?
Make sure you are running in Release mode (debug="false" in your web.config). Also notice that there is a bug in the RC that prevents of properly caching the resources server side and thus increasing the CPU usage of your site everytime a request is made to the url that is supposed to serve the bundled and minified version of your resources.

Resources