How to pack VueJS + NodeJS + MongoDB application in Docker

New-project-1

As you can see from the previous article, I worked with different projects. The first days in a new team usually go the same way: the back-end sits down to me and performs magical actions for installing and deploying the application. Docker is indispensable for front-end since the backend is often written on a wide range of PHP / Java / Python / C # stacks and the front does not need to distract the backend every time to install and deploy everything. Only in one place did I see a bunch of Docker-Jenkins with a transparent deploy, logs, screwed by self-tests.



A lot of detailed articles have been written about docker. This article will focus on deploying Single Page Application using VueJS / Vue Router, the server part as a RESTful API with NodeJS, and MongoDB is used as the database. Docker Compose is used to describe and run several container applications.



Why do I need a docker



Docker allows you to automate the process of deploying an application. The developer no longer needs to install programs on his own, to combat incompatibility of versions on his machine. It is enough to install Docker and drive 1-2 teams into the console. It is most convenient to do this on Linux.



Getting started



Install Docker and Docker compose



Folder structure



We create 2 folders for client and server applications. A file with the .yml extension is a Docker Compose config where application containers are defined and linked.

Docker-compose.yml:



version: "3" services: mongo: container_name: mongo hostname: mongo image: mongo ports: - "27017:27017" server: build: server/ #command: node ./server.js #   CMD  Dockerfile  /server ports: - "3000:3000" links: - mongo client: build: client/ #command: http-server ./dist #   CMD  Dockerfile  /client network_mode: host ports: - "8089:8089" depends_on: - server
      
      





We create 3 services in docker: for NodeJS, MongoDB and for statics on Vue. To connect the client to the server, depends on server is added. Links mongo is used to link MongoDB with the server API. Server, client, mongo - service names.



Client on VueJS



The / client folder contains the application on VueJS. Application created using Vue Cli . When building the image, the client application builds into a set of static files in the / dist folder. Dockerfile describes a set of commands for building an image:



 FROM node:10 WORKDIR /client COPY ./package*.json ./ RUN npm install RUN npm install -g http-server COPY . . RUN npm run build EXPOSE 8081 CMD ["npm", "test:dev"]
      
      





Please note that package.json is copied and installed separately from other project files. This is done for performance, so that the contents of the / node_modules folder are cached when the build is repeated. Each command line is cached separately.



Finally, when the container starts, the npm run dev



command is executed. This command is described in package.json:



 "scripts": { "test:dev": "http-server dist -p 8081 -c 1 --push-state" }
      
      





To run files from the / dist folder, http-server



installed globally, and spa-http-server



installed in dev-dependencies so that Vue Router works correctly. The –push-state flag redirects to index.html. The -c flag with a value of 1 second is added so that the http-server does not cache scripts. This is a test case; on a real project it is better to use nginx.



In the Vuex store, create the apiHost: 'http://localhost:3000'



field, where the NodeJS Api port is registered. The client part is ready. Now all requests from the client to the back go to this url.



NodeJS server API



In the /server



folder, create server.js and Dockerfile:



 FROM node:10 WORKDIR /server COPY ./package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["node", "server.js"]
      
      





Server.js specifies the url for the database const url = 'mongodb://mongo:27017/';



. We allow cross-domain requests from the client:



 const clientUrl = 'http://localhost:8081'; const corsOptions = { origin: clientUrl, optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204 }; app.use(cors()); app.all('/*', (req, res, next) => { res.header('Access-Control-Allow-Origin', clientUrl); res.header('Access-Control-Allow-Headers', 'X-Requested-With'); next(); }); app.get('/getProducts', cors(corsOptions), (req, res) => { products.getContent .then(data => res.json(data), err => res.json(err)); }); app.get('/getUsers', cors(corsOptions), (req, res) => { db.getUsers() .then(data => res.json(data), err => res.json(err)); });
      
      





Conclusion



Now go to the project directory and run docker-compose build



images and docker-compose up



to start the containers. The command will raise 3 containers: server, client, mongo . For a server on NodeJS, you can configure hot-reload by associating it with the user folder. And the client is under development to run locally with hot reload, running separately server and mongo . To start a separate service, just specify its name docker-compose up client



. Remember to sometimes do prune



and delete containers, networks, and images to free up resources.



The full code can be found here . The project is still under development.



All Articles