This tutorial series will show you, how you can turn an Xtext project into a web application.

In the previous parts of this series we've created an Xtext web application which provides a code editor and is capable of running our domain-specific-language in the browser.

The development of the web application happened on localhost. It's time to make our web application available to the world.


First and foremost, you will need to have access to a server on which you have root access.

If you want to use Apache as your web server, you are free to do so, but this tutorial will use NGINX. Make sure NGINX is installed on your server.

For Debian based systems:

sudo apt-get update
sudo apt-get install nginx

During this tutorial I will assume that your NGINX directory is located at /etc/nginx/.

Furthermore, I will assume that you have already copied the source code of the web application onto the production server. Everything I will explain in this tutorial happens on the production server using SSH.

Build the Frontend

Until now, by using the npm run serve command, we started our web application in development mode. But when we want to put it onto the web, it is recommended to actually build the application. The built version of the application will be executed in production mode.

During development, Vue provides a lot of warnings to help you with common errors and pitfalls. However, these warning strings become useless in production and bloat your app’s payload size. In addition, some of these warning checks have small runtime costs that can be avoided in production mode. - From the VueJS guide

If we put our application onto the server, this also means that the location of our backend changes. To differentiate between production and development environment, we can use the process.env.NODE_ENV variable. Open ConnectionData.js and change the baseURL in the following way:

// FILE: <project>/frontend/src/services/ConnectionData.js

module.exports = {
  baseUrl: (process.env.NODE_ENV === 'production') ? '<domain_or_IP>/api/' : 'localhost:8085/',
  protocol: 'http://'

If we now run the build command, the NODE_ENV variable is automatically set to 'production' by Vue and the frontend will use '<domain_or_IP>/api/' as the baseUrl. Otherwise, during development, Vue will use the backend on localhost.

Be sure to add the /api/ path at the end of your production baseUrl. NGINX will use this path as an indicator to pass the requests to the backend of the application.

To build the frontend for production run:

cd <project>/frontend/
npm run build

This will create a dist directory in our frontend project. It contains our web app, which is now ready to be delivered by NGINX.

The typical directory for web application is  the /var/www/html directory. This means we have to copy our built frontend into this directory, where NGINX can access it:

cp -R <project>/frontend/dist /var/www/html/dsl

The Application Backend

In the last step we will run the backend on the production server.

When running in production, Nodemon and ESLint should no longer be necessary.

To start our application in a more simple way, we extend our script section, with the start:prod command:

  "scripts": {
    "start": "./node_modules/nodemon/bin/nodemon.js src/app.js --exec 'npm run lint && node'",
    "start:prod": "NODE_ENV=production node src/app.js",
    "lint": "./node_modules/.bin/eslint src/",
    "test": "echo \"Error: no test specified\" && exit 1"

The backend itself does not need any changes. But we have to keep the backend process alive, even after closing our SSH connection. I will show you two solutions to that problem. The first and more simple one uses screen. The second and more complex one uses PM2, which is a specialized process manager.

Choose one but not both.


screen gives us the possibility to keep a session alive even after closing the terminal.

Make sure screen is installed:

$ sudo apt-get update
$ sudo apt-get install screen

The workflow can be described like so:

  1. Use SSH to connect to your production server.
  2. Open a screen session with screen -S dsl-backend
  3. Navigate to your application backend and start it as always with npm run start
  4. To detach your terminal from the session first press Ctrl+A and secondly just D.
  5. Exit your SSH connection. The backend will keep running.

To reattach to that session, e.g. to take a look at the application output, run screen -R dsl-backend. In order to list all the running screen sessions run screen -ls.

If you are ok with that solution to handle your processes, jump to the next chapter. Otherwise, take a look at PM2.


PM2 is a full-blown process management tool, which can monitor crashes, memory usage and it even can start processes in a cluster.

To install PM2 run:

npm install pm2 -g

PM2 uses its own configuration files for managing processes.

Create <project>/backend/ecosystem.config.js:

// FILE: <project>/backend/ecosystem.config.js

module.exports = {
  apps : [{
    name: 'DSL_backend',
    script: 'npm',
    args: 'run start:prod',
    autorestart: true,
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'development'
    env_production: {
      NODE_ENV: 'production'

You can now start your backend in the background by running the pm2 start ./ecosystem.config.js --env production command. The command will return a table of all currently running processes.

Some other useful commands:

# List processes of the currently logged in user 
$ pm2 ls

# Open monitoring console
$ pm2 monit

# stop/delete/restart process with <id>
$ pm2 [stop | delete | restart] <id>

There is so much more you can do with PM2. Just take a look at their documentation.

You probably restart your server from time to time, so it can be useful to configure auto start for the application backend in PM2.

With the backend running in PM2 run pm2 startup and follow the instructions given by PM2. To undo this command simply run pm2 unstartup.

Next we have to configure NGINX to handle the frontend and backend requests.

Configure NGINX

The web server, in our case NGINX, handles all incoming HTTP and HTTPS requests. In this chapter we will configure NGINX that it provides the built frontend and redirects the /api/ requests to our application backend.

Create the configuration file for NGINX at /etc/nginx/sites-available/dsl-webapp.conf:

# FILE: /etc/nginx/sites-available/dsl_webapp.conf

# configuration for the DSL front- and backend

server {
  # The IP address the server can be reached at.
  # Usually this parameter holds the domain name of the server.
  # In which directory is the index file when a client
  # visits the root path "http://<server>/"
  root /var/www/html/dsl;
  index index.php index.html index.cgi;

  # Default port the webserver is listining on. HTTP default port ist 80.
  listen 80 default_server; # IP v4 port config
  listen [::]:80 default_server; # IP v6 port config

  # The frontend is requesting /api/ to reach the backend
  # The application backend is listening on Port 8090,
  # so we pass these requests to this port.
  location /api/ {
    proxy_pass http://localhost:8085/;

If you take a look at the server_name parameter you will see that this configuration uses the IP of the server and therefore unencrypted HTTP communication. None of which is recommended. You should always use an actual domain name and an SSL/TLS certificate (e.g. from Let's Encrypt) for your web applications.

  • The root parameter tells NGINX where it can find the frontend index-file.
  • The location /api/ {...} section tells NGINX to pass every request, which wants something from the /api/ path, to localhost:8080. Be sure to append the last forward slash! See the documentation, to know why.

To activate this configuration, we have to create a symlink in /etc/nginx/sites-enabled to point to our prior created configuration file:

$ ln -s /etc/nginx/sites-available/dsl-webapp.conf /etc/nginx/sites-enabled/dsl-webapp.conf

Afterwards restart the NGINX server with systemctl restart nginx. To check if NGINX is running enter systemctl status nginx.

Conclusion Part 4

We did quite a lot by the end of part 4 and at this state of the series. We took an Xtext project, figured out how the web integration works, extracted the parts that we need and built a full stack web application. On top of that we made it available to the world by setting up an NGINX web server as a reverse proxy for our application.