1

I have a project that uses NodeJS as a server (with ExpressJS) and MySQL to handle databases. To load them both together, I am using Docker. Although this project includes a ReactJS client (and I have a client folder for the react and a server folder for the nodejs), I have tested communication between the server and client and it works. Here is the code that pertains to both the server and mysql services:

docker-compose.yml

mysql:
    image: mysql:5.7
    environment:
      MYSQL_HOST: localhost
      MYSQL_DATABASE: sampledb
      MYSQL_USER: gfcf14
      MYSQL_PASSWORD: xxxx
      MYSQL_ROOT_PASSWORD: root
    ports:
      - 3307:3306
    restart: unless-stopped
    volumes:
      - /var/lib/mysql
      - ./db/greendream.sql:/docker-entrypoint-initdb.d/greendream.sql
    .
    .
    .
server:
    build: ./server
    depends_on:
      - mysql
    expose:
      - 8000
    environment:
      API_HOST: "http://localhost:3000/"
      APP_SERVER_PORT: 8000
    ports:
      - 8000:8000
    volumes:
      - ./server:/app
    links:
      - mysql
    command: yarn start

Then there is the Dockerfile for the server:

FROM node:10-alpine

RUN mkdir -p /app
WORKDIR /app

COPY package.json /app
COPY yarn.lock /app

RUN yarn install
COPY . /app

CMD ["yarn", "start"]

In the server's package.json, the script start is simply this: "start": "nodemon index.js" And the file index.js that gets executed is this:

const express = require('express');
const cors = require('cors');
const mysql = require('mysql');

const app = express();

const con = mysql.createConnection({
  host: 'localhost',
  user: 'gfcf14',
  password: 'xxxx',
  database: 'sampledb',
});

app.use(cors());

app.listen(8000, () => {
  console.log('App server now listening on port 8000');
});

app.get('/test', (req, res) => {
  con.connect(err => {
    if (err) {
      res.send(err);
    } else {
      res.send(req.query);
    }
  })
});

So all I want to do for now is confirm that a connection takes place. If it works, I would send back the params I got from the front-end, which looks like this:

axios.get('http://localhost:8000/test', {
  params: {
    test: 'hi',
  },
}).then((response) => {
  console.log(response.data);
});

So, before I implemented the connection, I would get { test: 'hi' } in the browser's console. I expect to get that as soon as the connection is successful, but what I get instead is this:

{
  address: "127.0.0.1"
  code: "ECONNREFUSED"
  errno: "ECONNREFUSED"
  fatal: true
  port: 3306
  syscall: "connect"
  __proto__: Object
}

I thought that maybe I have the wrong privileges, but I also tried it using root as user and password, but I get the same. Weirdly enough, if I refresh the page I don't get an ECONNREFUSED, but a PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR (with a fatal: false). Why would this happen if I am using the right credentials? Please let me know if you have spotted something I may have missed

gfcf14
  • 316
  • 4
  • 30
  • Hi, perhaps there's a firewall or network issue? – jspcal Jun 25 '19 at 01:32
  • @jspcal I’m doing this on a linux mint at home and on a windows 10 at work. How could I check if there is a blockage of some sort? Also if both images are supposed to run on the same internal network created by docker-compose, is it really possible to have firewall issue? – gfcf14 Jun 25 '19 at 02:01
  • Use the port 3307 while connection – Valerian Pereira Jun 25 '19 at 02:14
  • Possible duplicate of [node.js mysql error: ECONNREFUSED](https://stackoverflow.com/questions/21206801/node-js-mysql-error-econnrefused) – mchawre Jun 25 '19 at 04:00

1 Answers1

2

In your mysql.createConnection method, you need to provide the mysql host. Mysql host is not localhost as mysql has its own container with its own IP. Best way to achieve this is to externalize your mysql host and allow docker-compose to resolve the mysql service name(in your case it is mysql) to its internal IP which is what we need. Basically, your nodejs server will connect to the internal IP of the mysql container.

Externalize the mysql host in nodejs server:

const con = mysql.createConnection({
  host: process.env.MYSQL_HOST_IP,
  ...
});

Add this in your server service in docker-compose:

environment:
      MYSQL_HOST_IP: mysql // the name of mysql service in your docker-compose, which will get resolved to the internal IP of the mysql container
rajesh-nitc
  • 5,049
  • 3
  • 25
  • 33
  • Thank you! It seems to work at the first time, but if I refresh I get `{code: "PROTOCOL_ENQUEUE_HANDSHAKE_TWICE", fatal: false}` – gfcf14 Jun 26 '19 at 00:14
  • I suspected the `HANDSHAKE_TWICE` error would be due to connecting whenever the `get` route is called, so I removed it from the `.get` and placed it outside, right below `.createConnection`. When I run, I get a `PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR` (because I added a simple mysql query). But oddly enough, if I change something in `server/index.js` and refresh the browser, it works – gfcf14 Jun 26 '19 at 00:30
  • 1
    I changed `.createConnection` to `.createPool` and it works like a charm. Again, thank you very much! – gfcf14 Jun 26 '19 at 00:39
  • Thank you, thank you so much. I don't know where else to search, your answer really solve problem. I've spent so much time looking for the answer. Thank you again – Fadil Natakusumah Oct 28 '21 at 07:15