If we want to use Microservice architecture, we will build Nginx, Express, PostgreSQL images first, and then use Docker Compose to run Nginx, Express, PostgreSQL at once.
Version
Vue 3.2
Nginx 1.21.3
Express 4.17.1
PostgreSQL 14.0
Architecture
Nginx and Express are all in Docker, and they expose ports 7979
and 8080
outside of Docker, but PostgreSQL is only available inside Docker.
Vue
Vue runs successfully on port 7979
.
App.vue
<script setup>
import axios from 'axios'
const API_SVR = import.meta.env.VITE_API_SVR
let articles = $ref ([])
let onClick = async _ => {
let { data } = await axios.get (`${API_SVR}/api/articles`)
articles = data
}
</script>
<template>
<button @click="onClick">Get Articles</button>
<ul>
<li v-for="x in articles">{{ x.id }}:{{ x.title }} / {{ x.content }}</li>
</ul>
</template>
Line 4
const API_SVR = import.meta.env.VITE_API_SVR
Read VITE_API_SVR
environment variable in .env
.
LIne 5
let articles = $ref ([])
Declare articles
reactive variable with default value []
.
Line 7
let onClick = async _ => {
let { data } = await axios.get (`${API_SVR}/api/articles`)
articles = data
}
Call http://localhost:8080/api/articles
to get articles.
Environment Variable
.env
VITE_API_SVR=http://localhost:8080
DB_SVR=Postgres
VUE_VER=0.0.0
EX_VER=0.0.0
NGINX_PORT=7979
EX_PORT=8080
HOST_DIR=.data
POSTGRES_PORT=5432
POSTGRES_DB=DBLab
POSTGRES_USER=admin
POSTGRES_PASSWORD=12345
Environment variable for Vue and Docker Compose :
VITE_API_SVR
: URL for API server, which Vue usesDB_SVR
: database server, which Express usesVUE_VER
: Vue build version, which Docker Compose usesEX_VER
: Express build version, which Docker Compose usesNGINX_PORT
: Nginx exposed port, which Docker Compose usesEX_PORT
: Express exposed port, which Docker Compose usesHOST_DIR
: host data directory mounted to PostgresPOSTGRES_PORT
: Postgre exposed port, which Express usesPOSTGRES_DB
: Postgre database, which Express and Postgre usePOSTGRES_USER
: Postgre user, which Express and Postgre usePOSTGRES_PASSWORD
: Postgre password, which Express and Postgre use
Dockerfile
FROM nginx:alpine
COPY dist /usr/share/nginx/html
- Build Vue image by Nginx image
- Copy all files in
dist
directory to/usr/share/nginx/html
in image
Docker Compose
docker-compose.yml
version: "3"
services:
nginx:
image: nginx:${VUE_VER}
container_name: Nginx
ports:
- ${NGINX_PORT}:80
restart: always
express:
image: express:${EX_VER}
container_name: Express
ports:
- ${EX_PORT}:8080
environment:
- DB_SVR=${DB_SVR}
- DB_PORT=${POSTGRES_PORT}
- DB_USER=${POSTGRES_USER}
- DB_PASSWORD=${POSTGRES_PASSWORD}
- DB_DATABASE=${POSTGRES_DB}
restart: always
postgres:
image: postgres:latest
container_name: Postgres
volumes:
- ${HOST_DIR}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
restart: always
Line 3
nginx:
image: nginx:${VUE_VER}
container_name: Nginx
ports:
- ${NGINX_PORT}:80
restart: always
- Run container by
nginx
image with version ports
: map port byNGINX_PORT
environment variable with internal port80
Line 9
express:
image: express:${EX_VER}
container_name: Express
ports:
- ${EX_PORT}:8080
environment:
- DB_SVR=${DB_SVR}
- DB_PORT=${POSTGRES_PORT}
- DB_USER=${POSTGRES_USER}
- DB_PASSWORD=${POSTGRES_PASSWORD}
- DB_DATABASE=${POSTGRES_DB}
restart: always
Run container by the
express
image with versionports
: map port byEX_PORT
environment variable with internal port8080
environment
:DB_SVR
: transferDB_SVR
Docker Compose environment variable toDB_SVR
Express environment variableDB_PORT
: transferPOSTGRES_PORT
Docker Compose environment variable toDB_PORT
Express environment variableDB_USER
: transferPOSTGRES_USER
Docker Compose environment variable toDB_USER
Express environment variableDB_PASSWORD
: transferPOSTGRES_PASSWORD
Docker Compose environment variable toDB_PASSWORD
Express environment variableDB_DATABASE
: transferPOSTGRES_DB
Docker Compose environment variable toDB_DATABASE
Express environment variable
Line 21
postgres:
image: postgres:latest
container_name: Postgres
volumes:
- ${HOST_DIR}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
restart: always
- Run container by latest
postgres
image volumns
: map host directory to store dataenvironment
:POSTGRES_USER
: transferPOSTGRES_USER
Docker Compose environment variable toPOSTGRES_USER
PostgreSQL environment variablePOSTGRES_PASSWORD
: transferPOSTGRES_PASSWORD
Docker Compose environment variable toPOSTGRES_PASSWORD
PostgreSQL environment variablePOSTGRES_DB
: transferPOSTGRES_DB
Docker Compose environment variable toPOSTGRES_DB
PostgreSQL environment variable
NPM Config
package.json
{
"name": "vue-express-postgre",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"serve": "vite preview",
"build-vue": "vite build",
"build-nginx": "yarn build-vue && docker build -t nginx:$npm_package_version .",
"build-express": "docker build -t express:$npm_package_version ./express",
"build": "yarn build-nginx && yarn build-express"
},
"dependencies": {
"axios": "^0.24.0",
"vue": "^3.2.6"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.6.0",
"@vue/compiler-sfc": "^3.0.5",
"vite": "^2.5.1"
}
}
LIne 7
"build-vue": "vite build",
Build Vue to the dist
directory.
Line 8
"build-nginx": "yarn build-vue && docker build -t nginx:$npm_package_version .",
Build Vue to the dist
directory and make Vue as Nginx image.
LIne 9
"build-express": "docker build -t express:$npm_package_version ./express",
Build Express to image.
Line 10
"build": "yarn build-nginx && yarn build-express"
Build Nginx and Express image at once.
Express
Express runs successfully on port 8080
.
App.js
import express from 'express'
import { config } from 'dotenv'
import cors from 'cors'
import Knex from 'knex'
if (process.env.NODE_ENV !== 'production')
config ()
let app = express ()
app.use (cors ())
app.use (express.json ())
let knex = Knex ({
client: 'pg',
connection: {
host: process.env.DB_SVR,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
},
searchPath: ['public']
})
app.get ('/api/articles', async (req, res) => {
let { rows } = await knex.raw ('SELECT * FROM articles')
res.send (rows)
})
app.listen (8080, _ => console.log ('Express listen on port: 8080'))
Line 6
if (process.env.NODE_ENV !== 'production')
config ()
If not in production
mode, Express will read the environment variable from .env
in the express
directory.
Line 13
let knex = Knex ({
client: 'pg',
connection: {
host: process.env.DB_SVR,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
},
searchPath: ['public']
})
Connect to Postgres by Knex, all environment variables are derived from Docker Compose.
Line 25
app.get ('/api/articles', async (req, res) => {
let { rows } = await knex.raw ('SELECT * FROM articles')
res.send (rows)
})
Use knex. raw
to run raw SQL to Postgres.
Environment Variable
.env
DB_SVR=localhost
DB_PORT=5432
DB_USER=admin
DB_PASSWORD=12345
DB_DATABASE=DBLab
Environment variable used by development mode.
Dockerfile
FROM node:lts-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY package.json ./package.json
RUN yarn install
COPY app.js .
CMD [ "node", "app.js" ]
- Build Express image by Node image
- Copy
app.js
to image - Set
NODE_ENV
environment variable toproduction
to indicateproduction
mode
NPM Config
{
"type": "module",
"name": "express",
"version": "0.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"knex": "^0.95.13",
"pg": "^8.7.1"
},
"devDependencies": {
"nodemon": "^2.0.14"
},
"scripts": {
"dev": "nodemon app.js"
}
}
Line 18
"dev": "nodemon app.js"
Run Express in development mode.
PostgreSQL
Environment Variable
.env
HOST_DIR=.data
POSTGRE_PORT=5432
POSTGRE_DB=DBLab
POSTGRE_USER=admin
POSTGRE_PASSWORD=12345
Environment variable used by development mode.
Docker Compose
docker-compose.yml
version: "3"
services:
postgres:
image: postgres:latest
container_name: Postgres
volumes:
- ${HOST_DIR}:/var/lib/postgresql/data
ports:
- ${POSTGRES_PORT}:5432
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
Postgres container used by development mode.
Development
$ yarn dev
Run yarn dev
in the root directory to run the dev server for Vue.
$ yarn dev
Run yarn dev
in the express
directory to run Express by Node.
$ docker-compose up -d
run docker-compose up -d
in the postgres
directory to run Postgres.
Production
$ yarn build
Run yarn build
to build Nginx image and Express image at once.
$ docker-compose up -d
Run docker-compose up -d
in the root directory to run Nginx, Express, Postgres at once.
Conclusion
- Nginx and Express expose their ports outside of Docker, but Postgres only expose its port inside of Docker, which Express only uses
- In the recommended architecture in this post, we can both have development mode and production mode at the same time