Separating the front-end and the API server
Introduction
JHipster is a "full-stack" development tool, and its goal is to make you work efficiently with your front-end code (Angular/React) and your back-end code (Spring Boot).
However, it is a common requirement to separate the front-end and the back-end codes, typically because they are developed by different teams and have a different lifecycle.
Please note that this isn't the default JHipster way of working: this isn't complex to do, and works well, but this is an advanced topic. If you are getting started with JHipster, we recommend that you begin by using our standard way of working.
Generating only a front-end or a back-end application
You can choose to generate only a JHipster back-end or JHipster front-end application. At generation time, this is only a matter of choosing flags which are described in our application generation documentation:
jhipster --skip-client
will only generate a back-end application (this is typically what JHipster microservices are)jhipster --skip-server [options]
will only generate a front-end application (e.g.jhipster --skip-server --db=sql --auth=jwt
)
This should only work well for monoliths, as this doesn't make much sense for microservices (which have no front-end anyway) and gateways (which are monoliths with the Spring Cloud Gateway service enabled).
Directory layout
JHipster uses the standard Maven directory layout. When working on the back-end, you can read the Maven standard directory layout documentation.
When working on the front-end, there are 2 directories you need to know:
src/main/webapp
is where the client application will be developedtarget/classes/static
is where your client application will be packaged
If you have separate teams working on the front-end and back-end, you have two solutions:
- Both teams can work on the same project. As the directories are separated, there won't have much conflicts between teams. To make things even cleaner, both teams could work on separate branches.
- The front-end code can be stored in a specific Git project, and then imported into the main back-end project as a Git sub-module. This would require to move the client-side build scripts.
HTTP requests routing and caching
Once the front-end and back-end have been separated, the issue will be how to handle HTTP requests:
- All API calls will use a
/api
prefix. If you are using Angular, there is also a specificSERVER_API_URL
constant, defined in thewebpack.common.js
configuration, that can enrich this prefix. For example, you can use"http://api.jhipster.tech:8081/"
as a back-end API server (If you do this, please read our documentation on CORS below). /index.html
should not be cached by the browser or server.- Calls to
/
that serve static assets (from the front-end)/app
(which contains the client-side application) and/content
(which contains the static content, like images and CSS) should be cached in production, as those assets are hashed. - Calls to
/i18n
can be cached but files itsefs not are hashed, url query string are used - Calls to a non-existent route should forward the request to
index.html
. This is normally handled in the backend throughClientForwardController
. When deploying the client separately, this needs to be configured. See the Angular or React documentation for several examples.
Using BrowserSync
In dev
mode, JHipster uses BrowserSync for hot-reload of the front-end application. BrowserSync has a proxy (here is its documentation) that will route requests from /api
to a back-end server (by default, http://127.0.0.1:8080
).
This only works in dev
mode, but this is a very powerful way of accessing different API servers from the front-end.
Using CORS
CORS (Cross-origin request sharing) allow to access different back-end servers with the same front-end, without configuring a proxy.
This is an easy-to-use solution, but it can be less secure in production.
JHipster provides out-of-the-box a CORS configuration:
- CORS can be configured using the
jhipster.cors
property, as defined in the JHipster common application properties - It is enabled by default in
dev
mode for monoliths and gateways. It is turned off by default for microservices as you are supposed to access them through a gateway. - It is turned off by default in
prod
mode, for security reasons.
Using NGinx
Another solution to separate the front-end and back-end codes is to use a proxy server. This is very common in production, and some teams also use this technique in development.
This configuration will change depending on your specific use-case, so this cannot be automated by the generator, here is below a working configuration.
Create a src/main/docker/nginx.yml
Docker Compose file:
version: '2'
services:
nginx:
image: nginx:1.15-alpine
volumes:
- ./../../../target/static:/usr/share/nginx/html
- ./nginx/site.conf:/etc/nginx/conf.d/default.conf
ports:
- "8000:80"
This Docker image will configure an NGinx server, that reads the static assets from target/static
: this is where the JHipster front-end application is generated by default. In production, you will probably have a specific folder for this.
It also reads a ./nginx/site.conf
file: this is a NGinx-specific configuration file.
Configuration lambda
Here is a sample site.conf
:
server {
listen 80;
index index.html;
server_name localhost;
error_log /var/log/nginx/error.log;
root /usr/share/nginx/html;
location /api {
proxy_pass http://api.jhipster.tech:8081/api;
}
location /management {
proxy_pass http://api.jhipster.tech:8081/management;
}
location /swagger-resources {
proxy_pass http://api.jhipster.tech:8081/swagger-resources;
}
location /v2 {
proxy_pass http://api.jhipster.tech:8081/v2;
}
location /auth {
proxy_pass http://api.jhipster.tech:8081/auth;
}
location / {
try_files $uri $uri/ /index.html;
}
}
This configuration means that:
- NGinx will run on port
80
- It will read the static assets in folder
/usr/share/nginx/html
, and - It will act as a proxy from
/api
tohttp://api.jhipster.tech:8081/api
- Any unhandled requests will forward to
index.html
This configuration will require some tuning depending on your specific needs, but should be a good enough starting point for most applications.