cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

NodeJS Express API Client network socket connection issue in Docker

I'm building an express api in front of a neo4j database. The whole project runs two services in docker:

  1. neo4j-database
  2. characters-api

I use an .env file to set all environment variables, hence the mapping in the docker-compose.yml.

Both are connected through a network called "neo4j-database-nw". Express (by default creation with the "express-generator") has the myapp/bin/www file as entrypoint. This is where I create my neo4j driver instance, which I also added to the export of the module, as the driver shall be available throughout the application.
When I create a controller for a route (e.g. /test) and and create a session in that module, which then runs a simple query, I get the following error: "Neo4jError: Client network socket disconnected before secure TLS connection was established".
In the myapp/bin/www file I create the driver with the following parameters:
const neo4jDriver = neo4j.driver(bolt://neo4j-database:7687, neo4j.auth.basic(neo4j, test));
And I close the driver not earlier than with the terminus package during the "onSignal" event, which is, when the application receives one of the following signals "['SIGTERM', 'SIGINT']".
What am I missing to establish a connection with the database from my nodejs application?

myapp/controllers/test/testController.ts:

import Debug from 'debug';
import express from 'express';
import { neo4jDriver } from '../../bin/www';

const debug = Debug('neo4j:session');
const router = express.Router();

/* GET users listing. */
router.get('/', async (req, res, next) => {
  const query = 'CREATE (c:Character) SET c.name = {name} RETURN c';

  const params = {};

  const session = neo4jDriver.session();

  try {
    const result = await session.run(query, params);
    res.status(200).send(result);
  } catch (error) {
    debug(`Unable to execute query. ${error}`);
    res.status(500).send(error);
  } finally {
    session.close();
  }
});

export default router;

docker-compose.yml:

version: "3.7"
services:
  characters-api:
    image: characters-api:latest
    container_name: characters-api
    build: .
    depends_on:
      - neo4j-database
    command: sh -c 'sh ./install-dependencies.sh && ./node_modules/.bin/tsc-watch --project ./tsconfig.json --skipLibCheck --noClear --onSuccess "npm run compile-and-run"'
    environment:
      NEO4J_API_CONTAINER_PORT: ${NEO4J_API_CONTAINER_PORT}
      NEO4J_API_HOST_PORT: ${NEO4J_API_HOST_PORT}
      NEO4J_USERNAME: ${NEO4J_USERNAME}
      NEO4J_PASSWORD: ${NEO4J_PASSWORD}
      NEO4J_DATABASE: ${NEO4J_DATABASE}
      NEO4J_BOLT_PORT: ${NEO4J_BOLT_PORT}
      NEO4J_HOSTNAME: ${NEO4J_HOSTNAME}
    ports:
      - ${NEO4J_API_HOST_PORT}:${NEO4J_API_CONTAINER_PORT}
    volumes:
      - .:/usr/src/app
    networks: 
      - neo4j-database-nw
  neo4j-database:
    image: neo4j:4.0.0
    container_name: neo4j-database
    environment:
      NEO4J_AUTH: ${NEO4J_AUTH}
    ports:
      - 7474:7474
      - 7473:7473
      - 7687:7687
    volumes:
      - ./neo4j-database/data:/data
      - ./neo4j-database/logs:/logs
      - ./neo4j-database/import:/var/lib/neo4j/import
      - ./neo4j-database/plugins:/plugins
    networks: 
      - neo4j-database-nw
networks: 
  neo4j-database-nw:

1 ACCEPTED SOLUTION

Hey Daniel, thank you for your help. I just updated the neo4j-driver to the latest version (4.0.1) and that did the trick...
The ^ semver marker in my package.json kept the package from upgrading to the latest major version.

View solution in original post

4 REPLIES 4

Please show what's behind this code, and indicate which version of the JS driver you're using.

I'm using "neo4j-driver": "^1.7.6", as well as node:12.15.0 and neo4j:4.0.0.
I've googled a lot and only found a similar issue, where the driver/session was closed before the application and the database had the chance to establish the TLS connection. But this doesn't seem to be the case here, i think.

../../bin/www:

#!/usr/bin/env node
'use strict';

/**
 * Module dependencies.
 * @private
 */

import { createTerminus, TerminusOptions } from '@godaddy/terminus';
import dotenv from 'dotenv';
import { createServer } from 'http';
import neo4j from 'neo4j-driver';
import app from '../app';
import { onError, onListening } from './helpers/expressEventHandlers';
import { normalizePort } from './helpers/normalizePort';
import { onSignal } from './helpers/terminusHandlers';

/**
 * Get environment variables.
 */

dotenv.config();
const neo4jHostname = process.env.NEO4J_HOSTNAME!;
const neo4jBoltPort = process.env.NEO4J_BOLT_PORT!;
const neo4jUsername = process.env.NEO4J_USERNAME!;
const neo4jPassword = process.env.NEO4J_PASSWORD!;

/**
 * Create the neo4j database driver.
 */

const neo4jDriver = neo4j.driver(`bolt://${neo4jHostname}:${neo4jBoltPort}`, neo4j.auth.basic(neo4jUsername, neo4jPassword));

/**
 * Get the port from environment and store it in Express.
 */

const port = normalizePort(process.env.NEO4J_API_CONTAINER_PORT || '3000');
app.set('port', port);

/**
 * Create the HTTP server.
 */

const server = createServer(app);

/**
 * Set the Terminus options
 */

const terminusOptions: TerminusOptions = {
  onSignal,
  signals: ['SIGTERM', 'SIGINT']
};

createTerminus(server, terminusOptions);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Module exports.
 * @public
 */

export {
  port,
  server,
  neo4jDriver
};

And the .env file:

NEO4J_CONLIMIT='50'
NEO4J_HOSTNAME=neo4j-database
NEO4J_HTTP_PORT=7474
NEO4J_HTTPS_PORT=7473
NEO4J_BOLT_PORT=7687
NEO4J_AUTH=neo4j/test
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=test
NEO4J_DATABASE='neo4j'
NEO4J_API_HOST_PORT=3000
NEO4J_API_CONTAINER_PORT=3000
NEO4J_API_DEBUG_PORT=9229

I think a thing to try here is to use v1. We're in the midst of some major updates to our client drivers & neo4j itself with version 4. Usually people do this:

const neo4j = require('neo4j-driver').v1

Or in your case:

import { v1 as neo4j } from 'neo4j-driver';

When using the older driver. What you're doing there looks legitimate for the new driver for 4.0 though, but that's not the version you're using.

Hey Daniel, thank you for your help. I just updated the neo4j-driver to the latest version (4.0.1) and that did the trick...
The ^ semver marker in my package.json kept the package from upgrading to the latest major version.