Connecting to Neo4j from a node.js ap

In this tutorial, we will walk through how to set up a quick node.js-based app and connect it to a local Neo4j instance, so we can start querying data.

A few assumptions about skill level

  • I assume you have the basic knowledge to set up a node project

  • Node js is installed on your machine

  • A running instance of Neo4j Desktop or container ready to accept connections

  • Basic knowledge of the Cypher query language

Step 1: A very basic node script

Open your text editor of choice, mine happens to be the ubiquitous VS Code, and create a new folder called node-loves-neo4j. Here we will initialise our new script and install a few packages to help us connect to Neo4j.

mkdir node-loves-neo4j
cd node-loves-neo4j
npm init -y

Now that we have our basic node.js package setup, let's go ahead and create the index file and install the neo4j-driver required for us to talk to the database.

touch index.js
npm install neo4j-driver

Step 2: Setting up the script

Open index.js in your favourite editor and add the following snippet of code. In the first line we are importing the neo4j-driver, so we can use it. Make sure your local Neo4j instance is running on port :7687, and has the neo4j-user and password setup (otherwise fill in credentials that match your configuration)

// index.js

// import the neo4j-driver module
import neo4j from 'neo4j-driver'

// create credentials to access the local database instance
const credentials = {
  host: 'neo4j://localhost:7687',
  user: 'neo4j',
  password: '<your-secret-password>'
}

// here we create the driver we will be using to access, using basic auhtentication
const _driver = neo4j.driver(credentials.host, neo4j.auth.basic(credentials.user, credentials.password))

Step 3: Making it easy to query

Now that we have our driver set up and ready to connect, let us create a little helper function to make our queries as clean as possible. Below the driver create the following function. Be sure to note that the function is async to allow us to await the promise returned from Neo.

// index.js

// our function will take a string query and an object of parameters.
// For this we will only use the queries, but I included the params 
// for good measure
const cypher = async (query, params) => {

  try {
    // first we will create a session
    const session = _driver.session()  

    // query the database for results
    const result = await session.run(query, params)

    // check if we had a result (possibly add some better error handling here)
    if (result.records.length == 0)
        return []

    // since we know know that we have data
    } catch(err) {
      console.error('error', err)
    } finally {
      await session.close()
      return result.records || []  
    }
}

Step 4: Create a few nodes

Great, now we have a function that we can use to make calls, so let's try it out in action by creating a small graph of rock and metal bands and the countries they are from. Here we are using Neo4j's built-in function UNWIND to iterate over a list of band names and create a node for each band, setting its name.

// American bands
await cypher(`
UNWIND ['Metallica', 'Slayer', 'Megadeth', 'Guns N Roses'] as bandName
MERGE (b:Band { name: bandName })
MERGE (c:Country { name: 'USA' })
MERGE (b)-[:ORIGINATES_FROM]->(c)
`)

// Aussie bands
await cypher(`
UNWIND ['AC/DC', 'Wolfmother'] as bandName
MERGE (b:Band { name: bandName })
MERGE (c:Country { name: 'Australia' })
MERGE (b)-[:ORIGINATES_FROM]->(c)
`) 

// British bands
await cypher(`
UNWIND ['Black Sabbath', 'Iron Maiden', 'The Who', 'The Rolling Stones'] as bandName
MERGE (b:Band { name: bandName })
MERGE (c:Country { name: 'England' })
MERGE (b)-[:ORIGINATES_FROM]->(c)
`)

Notice here, in stead of using the CREATE-keyword to create our nodes and relationships, we use the MERGE-keyword. Merge will either create the node if it does not exist or skip if it does. While a bit slower, MERGE allows us to rerun our script over and over, without having to worry about clashing relationships.

Step 5: Query the data

Now that we have a few nodes and relationships to work with, let's query and get a list of band names along with the country they are from and order them by the band's name.

// will return on the form { name: '<name>', country: '<country>' }
const result = await cypher(`
MATCH (b:Band)-->(c:Country)
RETURN b.name as name, c.name as country
ORDER BY b.name
`) 

// consloe.log each row from the result
result.forEach((r,i) => console.log(`Band ${i}: ${r.name}, ${r.country}`))

// Result:
// Band 0: AC/DC, Australia
// Band 1: Black Sabbath, England
// Band 2: Guns N Roses, England
// ...

As you can probably tell, the results are stored in an array and each item is an object with the named properties we selected from our graph. This makes it easy to grab the result and work with the object as we do with any other javascript object.

Step 6: Run the script

Last thing we need is to actually run our script, so let's go ahead and to that now.

$ node index.js
Band 0: AC/DC, Australia
Band 1: Black Sabbath, England
Band 2: Guns N Roses, England

Conclusion

So, there you have it. A simple script, that lets you connect to Neo4j and submit queries in an easy package so you can focus on your model and your app.

Hope you have enjoyed it and happy graph'ing :)

/Anders