how to configure testing on a docker node mysql app - mysql

I've created a node/mysql app and i have the following docker-compose.yaml file
version: "3.4"
x-common-variables: &common-variables
MYSQL_USER: sampleuser
MYSQL_PASSWORD: samplepassword
MYSQL_DATABASE: sampledb
services:
mysql-db:
image: mysql:5.7
container_name: mysql_container
environment:
<<: *common-variables
MYSQL_HOST: localhost
MYSQL_ROOT_PASSWORD: root
ports:
- 3308:3306
restart: always
volumes:
- ./db:/docker-entrypoint-initdb.d
phpmyadmin:
depends_on:
- mysql-db
image: phpmyadmin/phpmyadmin
container_name: phpadmin_container
environment:
PMA_HOST: mysql-db
links:
- mysql-db:mysql-db
ports:
- 8080:80
restart: always
server:
build: ./server
container_name: node_server_container
ports:
- 3000:3000
volumes:
- ./server:/app
working_dir: /app
depends_on:
- mysql-db
environment:
<<: *common-variables
MYSQL_HOST_IP: mysql-db
links:
- mysql-db
now, in the server sub directory i have a small node server
require('dotenv').config()
const express = require('express');
const setRouter = require('./routes/routes');
const port = process.env.PORT || 3000;
const app = express();
setRouter(app)
app.set('port', port);
app.use('/', (req, res) => {
return res.status(200).json({ statusCode: 200 })
});
// Listen on provided port, on all network interfaces.
app.listen(port, () => console.log(`API running on localhost:${port}`));
module.exports = app;
which connect to the mysql db:
'use strict';
const mysql = require('mysql2');
const options = {
host: process.env.MYSQL_HOST_IP,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
};
const pool = mysql.createPool(options);
const promisePool = pool.promise();
module.exports = promisePool;
I would like to test my endpoints, and i tried using jest and supertest but i failed miserably.
I'm trying something like this:
const app = require('../app')
const request = require('supertest');
describe('GET /users/:email', function() {
it('responds with json', async function(done) {
const res = await request(app).get('/users/Jupiter#planet.it')
expect(res.headers["Content-Type"]).toMatch(/json/);
});
});
Now, if i don't provide a .env i get a console log in my users route to print the error:
Error: Access denied for user ''#'localhost' (using password: NO)
if I provide a .env
MYSQL_HOST_IP=127.0.0.1:3306
MYSQL_USER=sampleuser
MYSQL_PASSWORD=samplepassword
MYSQL_DATABASE=sampledb
then I this error: Error: getaddrinfo ENOTFOUND 127.0.0.1:3306
How do i get to connect to the running container? (note that i run the test manually from the server folder after stopping the node container, not sure if there's a better solution)
Full repo can be found here

Related

Error: connect ECONNREFUSED 172.19.0.2:3306 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16)

I developed nodeapi using docker container and when I trying to connect mysql running 'docker-compose up' throw this error.
error
my index.js
const express = require('express')
const mysql = require('mysql2');
const mysqlConfig = {
host: "cckl_mysql_server",
user: "cckl_admin",
password: "cckl_root",
database: "cckl_db",
socketPath: '/var/run/mysqld/mysqld.sock'
}
let con = null
const app = express()
app.get('/connect', function (req, res) {
con = mysql.createConnection(mysqlConfig);
con.connect(function(err) {
if (err) throw err;
res.send('connected')
});
})
docker-compose.yml
version: "3.8"
services:
cckl-app:
container_name: cckl-backend
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:8080
volumes:
- .:/app
- '/app/node_modules'
depends_on:
- cckl_mysql_server
cckl_mysql_server:
image: mysql:8.0
environment:
- MYSQL_DATABASE=cckl_db
- MYSQL_USER=cckl_admin
- MYSQL_PASSWORD=cckl_root
- MYSQL_ROOT_PASSWORD=cckl_root
I am new in docker somebody hint me plz.

docker-compose, node issue connecting to mysql

I am trying to get node to connect to my local mysql from within docker-compose
I have verified node connects fine from the command line, and docker compose is loading node fine - I can access functions which don't use mysql via localhost
my docker compose file
version: '3.6'
services:
mysql:
container_name: mysql
image: mysql:8.0.29
ports:
- "3306:3306"
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_USER: user
MYSQL_PASSWORD: paass
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: cine
volumes:
- './services/db/schema:/docker-entrypoint-initdb.d'
node:
container_name: node
build:
context: ./services/node
dockerfile: Dockerfile-dev
volumes:
- './services/node:/usr/src/app'
- '/usr/src/app/node_modules'
ports:
- 3001:3001
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
depends_on:
- mysql
client:
container_name: client
labels:
traefik.frontend.passHostHeader: 'true'
build:
context: ./services/client
dockerfile: Dockerfile-dev
volumes:
- './services/client:/usr/src/app'
- '/usr/src/app/node_modules'
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
- REACT_APP_API_URL=${REACT_APP_API_URL}
depends_on:
- node
nginx:
container_name: nginx
build:
context: ./services/nginx
dockerfile: Dockerfile-dev
restart: always
ports:
- 3007:80
depends_on:
- node
- client
and my node file
const app = express();
const bodyParser = require('body-parser');
const PORT = 3001;
const nodemailer = require('nodemailer');
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
require('dotenv-extended').load();
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'usser',
password : 'pass!',
database : 'cine',
port : '3306',
socketPath: '/var/run/mysqld/mysqld.sock'
});
console.log('this works');
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/api', function (req, res) {
res.json({ response: "hello!" });
});
app.get('/api/listfilms', function (req, res) {
sql="select * from films;";
console.log(sql);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
console.log(results);
results=JSON.parse(JSON.stringify(results))
console.log(results);
if (results == ""){
var response="error";
var resp = [{
"id": 0,
"resp": response,
},];
}else{
var resp = [{
"id": results.id,
"resp": results.name,
},];
}
res.json(resp);
})
})
the error i get is
node | Error: connect ENOENT /var/run/mysqld/mysqld.sock
node | at PipeConnectWrap.afterConnect [as oncomplete] (net.js:1107:14)
I have looked at other similar posts and tried their recommendations but none appear to work
Thanks
In your node config file the host must be mysql not localhost. Since you have network configured in between containers, you just can use myql as the host of node config file

MySQL docker container HostNotFoundError with Sequelize and node.js

I am trying to use my MySQL docker container with node.js using Sequelize. But I keep getting HostNotFoundError. I am able to connect to the database with MySQL Workbench.
I also tried putitng "127.0.0.1 localhost" into my host file.
Error:
C:\projects\todo-backend\node_modules\sequelize\lib\dialects\mysql\connection-manager.js:96
throw new SequelizeErrors.HostNotFoundError(err);
^
HostNotFoundError [SequelizeHostNotFoundError]: getaddrinfo ENOTFOUND
"localhost"
docker-compose.yml
version: '3.8'
networks:
mysql_app:
external: true
x-common-variables: &common-variables
MYSQL_USER: todo
MYSQL_PASSWORD: s3cret
MYSQL_DATABASE: todos
REACT_APP_SERVER_PORT: 8000
services:
mysql:
image: mysql
restart: always
environment:
<<: *common-variables
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: todos
ports:
- "3307:3306"
networks:
- mysql_app
volumes:
- C:/projects/docker/mysql:/data/pc
.env:
MYSQL_DB: "todos",
MYSQL_USERNAME: "todo",
MYSQL_PASSWORD: "s3cret",
MYSQL_HOST: "localhost",
MYSQL_DIALECT: "mysql"
MYSQL_PORT: 3307
Here I already tried "mysql", "localhost" and "127.0.0.1".
db.config.js:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.MYSQL_DB, process.env.MYSQL_USERNAME, process.env.MYSQL_PASSWORD, {
host: process.env.MYSQL_HOST,
dialect: process.env.MYSQL_DIALECT,
operatorsAliases: 0,
port: process.env.MYSQL_PORT,
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.todo = require("../model/todo.model.js")(sequelize, Sequelize);
module.exports = db;
When I remove the host then it tries to connect to 172.19.0.1, which is the ip-address inside the docker container.
server.js
require('dotenv').config()
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const PORT = process.env.PORT || 8080;
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const db = require("./app/config/db.config.js");
db.sequelize.sync({ force: true }).then(() => {
console.log("Drop and Resync with { force: true }");
});
require("./app/route/todo.route.js")(app);
app.listen(PORT, () => {
console.log(`Server is running on port: ${PORT}`)
});

mysql in docker won't connect with my nodejs API on my host

it has been a long day and i don't know why i am stuck at this problem for such a long time so any help is very much appreciated!
my problem is the famus code: 'ECONNREFUSED' which suddenly appeared for my project. i am trying to run a mysql db with PMA in docker and trying to connect to it from my nodejs (not in docker but on host) to get a fetch response but everything that i try and any suggestion i look at i find no solution! so any idea at this point would help me greatly and get me to bed sooner.
i cut the story short and put my codes here please do let me know with any tips, thanks in advance!
this is my server.js file
const express = require('express');
const apiroutes = require('./routes');
const app = express();
app.use(express.json());
app.use('/api/data', apiroutes);
app.listen('3000', ()=>{
console.log('server is running on port: 3000');
});
this is my routes file
const express = require('express');
const db = require('../db');
const router = express.Router();
router.get('/', async(req, res, next)=>{
try{
let results = await db.all();
res.json(results);
} catch(error){
console.log(error);
res.sendStatus(500);
}
});
module.exports = router;
this is my db file
const mysql = require('mysql');
const pool = mysql.createPool({
connectionLimit : 100,
password: 'secret',
user: 'user',
database: 'db_test',
host: 'localhost',
port: '3306'
});
let wholeDB = {};
wholeDB.all = () => {
return new Promise((resolve, reject)=>{
pool.query(`SELECT * FROM table1`,(error, result)=>{
if(error){
return reject(error);
}
return resolve(result);
});
});
};
module.exports = wholeDB;
this is my dockerfile
FROM php:7.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql
and finaly this is my docker-compose file
version: '3'
networks:
vuePhP:
services:
nginx:
image: nginx:stable-alpine
container_name: nginxWebServer
ports:
- "8088:80"
volumes:
- ./src:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- mysql
- php
networks:
- vuePhP
mysql:
image: mysql
container_name: mysqlService
restart: unless-stopped
tty: true
ports:
- "4306:3306"
volumes:
- ./mysql:/var/lib/mysql
environment:
MYSQL_DATABASE: db_test
MYSQL_USER: user
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: root
networks:
- vuePhP
phpmyadmin:
image: phpmyadmin
container_name: phpMyAdmin
restart: always
depends_on:
- mysql
- php
environment:
- PMA_HOST=mysql
- PMA_PORT=3306
ports:
- "8081:80"
networks:
- vuePhP
php:
build:
context: .
dockerfile: Dockerfile
container_name: phpService
volumes:
- ./src:/var/www/html
ports:
- "9000:9000"
networks:
- vuePhP
and this is the error that i get when i use postman to do a get command
errno: -111, code: 'ECONNREFUSED', syscall: 'connect',
address: '127.0.0.1', port: 3306, fatal: true
Error: connect ECONNREFUSED 127.0.0.1:3306
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16)
thank you!
edit: i have added the port 4306 to the node app and get this error :
code: 'ER_NOT_SUPPORTED_AUTH_MODE', errno: 1251, sqlMessage:
'Client does not support authentication protocol requested by server;
consider upgrading MySQL client', sqlState: '08004', fatal: true
i added this line in my compose file but it didn't help:
mysql:
image: mysql
container_name: mysqlService
restart: unless-stopped
tty: true
ports:
- "4306:3306"
volumes:
- ./mysql:/var/lib/mysql
environment:
MYSQL_DATABASE: KFV_test
MYSQL_USER: user
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: root
command: --default-authentication-plugin=mysql_native_password
networks:
- vuePhP
ah i found it, the problem is the version of mysql in composer and usage of mysql library instead of mysql2 as Phil graciously mentioned. so i downgraded mysql version by version until i found the one that worked and before that added mysql2 (npm i mysql2) to my nodejs app. and it worked. here my is my changes in docker-compose file. btw changed the ports as well not that it helped before the downgrade.
mysql:
image: mysql:5.7.24
container_name: mysqlService
restart: unless-stopped
tty: true
ports:
- "3306:3306"
volumes:
- ./mysql:/var/lib/mysql
environment:
MYSQL_DATABASE: db_test
MYSQL_USER: user
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: root
networks:
- vuePhP
and changed the connection method in my db file:
const mysql2 = require('mysql2');
const connection = mysql2.createConnection({
host: 'localhost',
user: 'user',
password: 'secret',
database: 'db_test',
});
let wholeDB = {};
wholeDB.all = () => {
return new Promise((resolve, reject)=>{
connection.query(`SELECT * FROM table1`,(error, result)=>{
if(error){
return reject(error);
}
return resolve(result);
});
});
};
module.exports = wholeDB;
var mysql = require('mysql')
var connection = mysql.createPool({
host: "localhost",
user: "root",
password: "",
database: "dbName",
port : "3306"
})
connection.getConnection((err, connection) => {
if (err) {
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
console.error('Database connection was closed.')
}
if (err.code === 'ER_CON_COUNT_ERROR') {
console.error('Database has too many connections.')
}
if (err.code === 'ECONNREFUSED') {
console.error('Database connection was refused.')
}
}
if (connection) connection.release()
return
})
module.exports = connection
example :
import connection from '../Modules/connection.js';
let LoginQuery = (values) => {
return new Promise((resolve, reject) => {
// let sql = `SELECT * FROM user_tbl WHERE mobile_number ="${values.mobile_number}" OR email_id="${values.mobile_number}"`;
let sql = "SELECT * FROM `admin_tbl` WHERE mobile_number = ? OR email_id = ?";
connection.query(sql, Object.values(values), (err, result) => {
console.log(err);
err ? reject(err) : resolve(result);
});
});
};
Please use this connecting pool hope it'll work.

Failed to fetch with Docker, MySQL, Express and TypeScript

I tried to create a todo-app with Node.js, TypeScript, MySQL.
This app works well. There is no issue.
However, when I tried to have this app on Docker Container, I have the following error.
Failed to load resource: net::ERR_NAME_NOT_RESOLVED
TypeError: Failed to fetch
at getTodos (ListTodo.tsx:16)
at ListTodo.tsx:25
at invokePassiveEffectCreate (react-dom.development.js:23487)
at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
at invokeGuardedCallback (react-dom.development.js:4056)
at flushPassiveEffectsImpl (react-dom.development.js:23574)
at unstable_runWithPriority (scheduler.development.js:468)
at runWithPriority$1 (react-dom.development.js:11276)
at flushPassiveEffects (react-dom.development.js:23447)
at react-dom.development.js:23324
at workLoop (scheduler.development.js:417)
at flushWork (scheduler.development.js:390)
at MessagePort.performWorkUntilDeadline (scheduler.development.js:157)
I failed to fetch data.
The backend code is:
//index.ts
import express from 'express';
import cors from 'cors';
import { Express, Request, Response } from 'express';
import { createPool, RowDataPacket } from 'mysql2';
const pool = createPool({
host: 'mysqldb',
port: 3306,
user: 'root',
password: 'pass123',
database: 'todo',
});
const app: Express = express();
app.use(cors());
app.use(express.json());
app.get("/todos", async (req: Request, res: Response) => {
try{
const resp = await pool.promise().query<RowDataPacket[]>(
'SELECT * FROM todo'
);
res.json(resp[0]);
} catch (error) {
throw error;
}
});
app.listen(5000);
The frontend code is:
import React, { useState, useEffect } from 'react';
export const ListTodo = () => {
const [todos, setTodos] = useState([]);
const getTodos = async () => {
try {
//fetch data
const response = await fetch('http://api-prod:5000/todos');
const jsondata = await response.json();
setTodos(jsondata);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getTodos()
}, []);
return (
//list data
);
};
docker-compose file is:
//docker-compose.yml
version: "3"
services:
mysqldb:
platform: linux/x86_64
image: mysql:8.0
hostname: mysqldb
environment:
- MYSQL_ROOT_PASSWORD=pass123
- MYSQL_DATABASE=todo
ports:
- 3306:3306
command: --default-authentication-plugin=mysql_native_password
networks:
- shared-network
volumes:
- db-config:/etc/mysql
- db-data:/var/lib/mysql
- ./db/backup/files:/data_backup/data
nginx:
image: nginx
container_name: nginx_prod
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- 80:80
depends_on:
api-prod:
condition: service_healthy
app-prod:
condition: service_started
networks:
- shared-network
api-prod:
container_name: server_api_prod
build:
context: server
dockerfile: Dockerfile
hostname: api-prod
depends_on:
- mysqldb
networks:
- shared-network
ports:
- 5000:5000
restart:
unless-stopped
healthcheck:
test: ["CMD", "curl", "http://api-prod:5000"]
interval: 5s
timeout: 3s
retries: 6
app-prod:
container_name: client_app_prod
build:
context: client
dockerfile: Dockerfile
hostname: app-prod
ports:
- 3000:3000
networks:
- shared-network
restart:
unless-stopped
networks:
shared-network:
volumes:
db-config:
db-data:
Why I fail to fetch data?
I think I have a correct database connection(host:mysqldb, port:3306).
Your React app runs in your browser, where you can't use the Docker hostnames. Those are only available on the Docker bridge network.
So your React app can't resolve the name api-prod. You need to use a name that can be resolved from the browser. If you run the app locally, you can use localhost. If you want to deploy it and use it from other computers, you need to use a name that can be resolved from those computers.
But for running locally
fetch('http://localhost:5000/todos')
should do the trick.