Using Docker & Docker Compose to Create an Express/Neo4J Dev Environment(Intro to Graph Databases)
May 03, 2021
Previous Content on Docker
- Video: Intro to Docker
- Video: Docker 101
- Video: Using Neo4j Database using Docker
- Video: Create a VSCode dev environment with Docker
- Blog: Building a C# API and Deploying to Heroku with Docker
- Deploying a Deno App using Docker and Fly.io
What we will do
The beauty of Docker is being able to create isolated containers with our application. With Docker Compose we can orchestrate how these containers work with each other. Imagine having your app in one container and your database in another and making them communicate with each other in a uniform and easy-to-replicate way.
This is exactly what we’ll do today.
- Create an express application and containerize it with Docker and Docker Compose
- Add a Graph Database with Neo4j
- Have our express app make calls to the database
Setup
- Must have nodeJS, Docker, and Docker Compose installed
- start a new folder and create the following folder structure and files
ROOT FOLDER
-> /services
-> /express
-> Dockerfile
-> docker-compose.yml
Create the Express App
- Open your terminal in your express folder
- Create a new node project
npm init -y
- install express
npm install express
- in package.json add the
"type":"module"
property, this is only supported in newer versions of node - also in package.json make sure there is a start script that reads
"start": "node server.js"
- create a file called
server.js
with the following
// import dependencies
import express from "express"
// create application object
const app = express()
//home route
app.get("/", (req, res) => {
res.send("<h1>Hello World</h1>")
})
// Server Listener
app.listen(5000, () => console.log("listening on 5000"))
- test this out locally by running the command
npm start
and going to localhost:5000, once confirmed, turn off the server with ctrl+c
Dockerizing Our Express App
So we will create a docker file that specifies how to set up our containerized environment for our node app. This will consist of…
- getting a base image
- setting a work directory (where the container is working from within itself)
- copy the of our project over (except node_modules)
- install dependencies
- specify an entrypoint command (command to run when the container is turned on)
- create a
.dockerignore
file with the following in the express folder (works like.gitignore
but for docker)
.env
/node_modules
- create an entrypoint.sh file in the express folder with the following
#!/bin/sh
# The comment abode is a shebang that tells the OS which binary to run the script with
# The exec command below is how this script should end so it goes back and executes the command specified in the docker-compose.yml file
exec "$@"
- make the entrypoint.sh executable by running
chmod +x entrypoint.sh
- Put the following in the
dockerfile
in the express folder
# Base image to start from
# A node image using the latest version using Alpine Linux 3.11
FROM node:current-alpine3.11
# Set the Working Directory the Containers Terminal Starts from
WORKDIR /usr/src/app
# Copy Project Over from this folder "." to "/usr/src/app/" in the container
COPY . /usr/src/app/
# install dependencies
RUN npm install
# Kick start the container with the entrypoint script
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
- create a
.env.express
file in the root folder - add the following to docker-compose.yml
version: "3.7"
# The different services that make up our "network" of containers
services:
# Express is our first service
express:
# The location of dockerfile to build this service
build: ./services/express
# Command to run once the Dockerfile completes building
command: npm start
# Volumes, mounting our files to parts of the container
volumes:
- ./services/express:/usr/src/app/
# Ports to map, mapping our port 5000, to the port 5000 on the container
ports:
- 5000:5000
# designating a file with environment variables
env_file:
- ./.env.express
- Open terminal up to the root folder and run the command
docker-compose up --build
this tells Docker to build the network detailed in the docker-compose.yml and build any images specified in the different services. - Go to localhost:5000 and double-check we still get our hello world.
- in a separate terminal in the same folder, let’s turn off the containers and remove their volumes
docker-compose down -v
Setting Up Neo4j
- Update
docker-compose.yml
version: "3.7"
# The different services that make up our "network" of containers
services:
# Express is our first service
express:
# The location of dockerfile to build this service
build: ./services/express
# Command to run once the Dockerfile completes building
command: npm start
# Volumes, mounting our files to parts of the container
volumes:
- ./services/express:/usr/src/app/
# Ports to map, mapping our port 5000, to the port 5000 on the container
ports:
- 5000:5000
# designating a file with environment variables
env_file:
- ./.env.express
# Tell docker this container depends on the neo service so they can communicate, the neo4j server will be located at neo:7474
depends_on:
- neo
## Defining the Neo4j Database Service
neo:
# The image to use
image: neo4j:latest
# map the ports so we can check the db server is up
ports:
- 7474:7474
- 7687:7687
# mounting a named volume to the container to track db data
volumes:
- neo4j_data:/data/
env_file:
- .env.neo4j
## volumes to be generated, these are saved somewhere for repeated use by docker
volumes:
neo4j_data:
- create a
.env.neo4j
file in the root with the following:
NEO4J_AUTH=none
- Rebuild our network
docker-compose up --build
- Head over to localhost:7474 and you should see the neo4j browser page and select “no authentication” and login to the default database
- At the top of the screen is a bar to enter queries using the Cypher Query Language (CQL).
- create a node
CREATE(result: Person{name:"Alex Merced",age:35}) RETURN result;
(This will create a person node) - create another node
CREATE(result: Activity{name: "coding"}) RETURN result
- create an edge(relationship) between the two nodes
MATCH(p:Person),(a:Activity) WHERE p.name = 'Alex Merced' AND a.name = 'coding' CREATE (p)-[relationship:likes]->(a) RETURN type(relationship);
- Let’s search for the person again
Match(p:Person) Where p.name='Alex Merced' Return p;
- Now with the person node visible you can hover over it and click on the icon to see its edges and you’ll see Alex Merced “likes” Coding. Welcome to the world of Graph Databases!
- Use CTRL + C to turn off your containers, no need to remove the volumes so our nodes will still exist when we turn them back on!
Using Neo4j in Express
- open your terminal to the express folder and install the neo4j drivers
npm install neo4j-driver
- update server.js like so!
// import dependencies
import express from "express"
import neo4j from "neo4j-driver"
// create application object
const app = express()
// connect to data
const db = neo4j.driver("bolt://neo:7687")
//home route
app.get("/", async (req, res) => {
const result = await db
.session({
database: "neo4j",
defaultAccessMode: neo4j.session.WRITE,
})
.run("Match(p:Person) Where p.name='Alex Merced' Return p;")
res.json(await result)
})
// Server Listener
app.listen(5000, () => console.log("listening on 5000"))
- run your container and see if you see your query on localhost:5000
YOU DID IT! You set up a network with a node application and neo4j database using Docker!