gfcf14
gfcf14

Reputation: 350

ECONNREFUSED when trying to connect NodeJS app to MySQL image via docker-compose

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

Upvotes: 1

Views: 2383

Answers (1)

rajesh-nitc
rajesh-nitc

Reputation: 5549

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

Upvotes: 3

Related Questions