258 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						|
tags:
 | 
						|
  - docker
 | 
						|
  - SQL
 | 
						|
  - node-js
 | 
						|
  - databases
 | 
						|
---
 | 
						|
 | 
						|
# Docker example: NodeJS backend with MySQL database
 | 
						|
 | 
						|
We will utilise [Docker Compose](Docker_Compose.md) to combine
 | 
						|
two containers:
 | 
						|
 | 
						|
- A container for the NodeJS backend
 | 
						|
- A container for the MySQL database
 | 
						|
 | 
						|
We will only create a Dockerfile for the NodeJS part since the existing `mysql`
 | 
						|
image is sufficient for most needs and does not require a specific
 | 
						|
configuration.
 | 
						|
 | 
						|
Each of the files listed below would be saved to the same source directory which
 | 
						|
would then form the basis of the
 | 
						|
[build context](Creating_a_Docker_image.md#creating-a-docker-image).
 | 
						|
 | 
						|
## Docker Compose file
 | 
						|
 | 
						|
```yml
 | 
						|
# docker-compose.yml
 | 
						|
version: "3.8"
 | 
						|
services:
 | 
						|
  db:
 | 
						|
    image: mysql:8.0
 | 
						|
    container_name: mysql_container
 | 
						|
    environment:
 | 
						|
      MYSQL_ROOT_PASSWORD: your_root_password
 | 
						|
      MYSQL_DATABASE: your_database_name
 | 
						|
      MYSQL_USER: your_database_user
 | 
						|
      MYSQL_PASSWORD: your_database_password
 | 
						|
    volumes:
 | 
						|
      - mysql-data:/var/lib/mysql
 | 
						|
    ports:
 | 
						|
      - "3306:3306"
 | 
						|
  app:
 | 
						|
    build: .
 | 
						|
    container_name: node_app
 | 
						|
    volumes:
 | 
						|
      - .:/usr/src/app
 | 
						|
    environment:
 | 
						|
      MYSQL_HOST: db
 | 
						|
      MYSQL_USER: your_database_user
 | 
						|
      MYSQL_PASSWORD: your_database_password
 | 
						|
      MYSQL_DB: your_database_name
 | 
						|
    depends_on:
 | 
						|
      - db
 | 
						|
    ports:
 | 
						|
      - "3000:3000"
 | 
						|
 | 
						|
volumes:
 | 
						|
  mysql-data:
 | 
						|
```
 | 
						|
 | 
						|
## Dockerfile for the NodeJS backend
 | 
						|
 | 
						|
```Dockerfile
 | 
						|
# Dockerfile
 | 
						|
FROM node:14
 | 
						|
 | 
						|
WORKDIR /usr/src/app
 | 
						|
 | 
						|
COPY package*.json ./
 | 
						|
 | 
						|
RUN npm install
 | 
						|
 | 
						|
COPY . .
 | 
						|
 | 
						|
EXPOSE 3000
 | 
						|
 | 
						|
CMD [ "node", "app.js" ]
 | 
						|
```
 | 
						|
 | 
						|
## NodeJS project setup
 | 
						|
 | 
						|
```json
 | 
						|
// package.json
 | 
						|
{
 | 
						|
  "name": "node-mysql-docker",
 | 
						|
  "version": "1.0.0",
 | 
						|
  "description": "Node.js and MySQL with Docker",
 | 
						|
  "main": "app.js",
 | 
						|
  "scripts": {
 | 
						|
    "start": "node app.js"
 | 
						|
  },
 | 
						|
  "dependencies": {
 | 
						|
    "express": "^4.17.1",
 | 
						|
    "mysql2": "^2.3.2"
 | 
						|
  }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
```js
 | 
						|
// app.js
 | 
						|
const express = require("express");
 | 
						|
const mysql = require("mysql2/promise");
 | 
						|
 | 
						|
const app = express();
 | 
						|
 | 
						|
const { MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB } = process.env;
 | 
						|
 | 
						|
const createConnection = async () => {
 | 
						|
  return await mysql.createConnection({
 | 
						|
    host: MYSQL_HOST,
 | 
						|
    user: MYSQL_USER,
 | 
						|
    password: MYSQL_PASSWORD,
 | 
						|
    database: MYSQL_DB,
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
app.get("/", async (req, res) => {
 | 
						|
  const connection = await createConnection();
 | 
						|
  const [rows] = await connection.query("SELECT 1 + 1 AS solution");
 | 
						|
  res.send(`Hello World! The solution is ${rows[0].solution}`);
 | 
						|
});
 | 
						|
 | 
						|
const PORT = 3000;
 | 
						|
app.listen(PORT, () => {
 | 
						|
  console.log(`Server is running on port ${PORT}`);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
To start up the environment you would then run:
 | 
						|
 | 
						|
```
 | 
						|
docker-compose -up
 | 
						|
```
 | 
						|
 | 
						|
## Environments
 | 
						|
 | 
						|
In the example, the database connection information in the Node source is coming
 | 
						|
from the
 | 
						|
[`process.env`](Managing_environments_in_NodeJS.md)
 | 
						|
object, which itself is sourcing the values `MYSQL_HOST`, `MYSQL_PASSWORD` etc
 | 
						|
from the Docker compose file. Therefore these values are hardcoded there.
 | 
						|
 | 
						|
This is not good practice as it exposes sensitive information and make managing
 | 
						|
different deployment environments (development, stage, test etc.) difficult.
 | 
						|
 | 
						|
To get around this we would create an `.env` file in the project directory that
 | 
						|
is Git ignored:
 | 
						|
 | 
						|
```sh
 | 
						|
# .env
 | 
						|
MYSQL_ROOT_PASSWORD=your_root_password
 | 
						|
MYSQL_DATABASE=your_database_name
 | 
						|
MYSQL_USER=your_database_user
 | 
						|
MYSQL_PASSWORD=your_database_password
 | 
						|
```
 | 
						|
 | 
						|
Then the `docker-compose.yml` file can be updated to use these variables:
 | 
						|
 | 
						|
```yml
 | 
						|
version: "3.8"
 | 
						|
services:
 | 
						|
  db:
 | 
						|
    image: mysql:8.0
 | 
						|
    container_name: mysql_container
 | 
						|
    environment:
 | 
						|
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
 | 
						|
      MYSQL_DATABASE: ${MYSQL_DATABASE}
 | 
						|
      MYSQL_USER: ${MYSQL_USER}
 | 
						|
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
 | 
						|
    volumes:
 | 
						|
      - mysql-data:/var/lib/mysql
 | 
						|
    ports:
 | 
						|
      - "3306:3306"
 | 
						|
  app:
 | 
						|
    build: .
 | 
						|
    container_name: node_app
 | 
						|
    volumes:
 | 
						|
      - .:/usr/src/app
 | 
						|
    environment:
 | 
						|
      MYSQL_HOST: db
 | 
						|
      MYSQL_USER: ${MYSQL_USER}
 | 
						|
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
 | 
						|
      MYSQL_DB: ${MYSQL_DATABASE}
 | 
						|
    depends_on:
 | 
						|
      - db
 | 
						|
    ports:
 | 
						|
      - "3000:3000"
 | 
						|
 | 
						|
volumes:
 | 
						|
  mysql-data:
 | 
						|
```
 | 
						|
 | 
						|
`${VARIABLE_NAME}` syntax is used to reference environment variables from the
 | 
						|
.env file in the `docker-compose.yml` file. Docker Compose will automatically
 | 
						|
load the variables from the .env file when starting the services.
 | 
						|
 | 
						|
### Development, staging, production environments
 | 
						|
 | 
						|
To specify different connection details for different environments you would
 | 
						|
create different `.env` files for each:
 | 
						|
 | 
						|
- `.env.development`
 | 
						|
- `.env.staging`
 | 
						|
- `.env.production`
 | 
						|
 | 
						|
Each file will contain **environment-specific variables**, such as database
 | 
						|
credentials, API keys, and other configuration details.
 | 
						|
 | 
						|
For example, development and production:
 | 
						|
 | 
						|
```yml
 | 
						|
# docker-compose.development.yml
 | 
						|
version: '3.8'
 | 
						|
services:
 | 
						|
  db:
 | 
						|
    ...
 | 
						|
  app:
 | 
						|
    ...
 | 
						|
    environment:
 | 
						|
      MYSQL_HOST: db
 | 
						|
      MYSQL_USER: ${MYSQL_USER}
 | 
						|
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
 | 
						|
      MYSQL_DB: ${MYSQL_DATABASE}
 | 
						|
      NODE_ENV: development
 | 
						|
 | 
						|
```
 | 
						|
 | 
						|
```yml
 | 
						|
# docker-compose.production.yml
 | 
						|
version: '3.8'
 | 
						|
services:
 | 
						|
  db:
 | 
						|
    ...
 | 
						|
  app:
 | 
						|
    ...
 | 
						|
    environment:
 | 
						|
      MYSQL_HOST: db
 | 
						|
      MYSQL_USER: ${MYSQL_USER}
 | 
						|
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
 | 
						|
      MYSQL_DB: ${MYSQL_DATABASE}
 | 
						|
      NODE_ENV: production
 | 
						|
`
 | 
						|
```
 | 
						|
 | 
						|
Then you would select the specific environment with your run command:
 | 
						|
 | 
						|
```
 | 
						|
docker-compose -f docker-compose.development.yml up -d
 | 
						|
```
 | 
						|
 | 
						|
Docker won't know by default which `.env` file to use from that command however.
 | 
						|
Assuming all the files are in the same directory you can use Bash substitution
 | 
						|
to specify the source of the environment specific variables:
 | 
						|
 | 
						|
```sh
 | 
						|
export $(cat .env.development | xargs) && docker-compose -f docker-compose.development.yml up -d
 | 
						|
```
 |