Developer Center » Languages » Javascript » Code Guides » Javascript Code Demo using Express

Javascript Code Demo using Express

All of the code shown is available as a simple Node.js application using Express. This application uses a ready-to-use demo database with the goodreads dataset containing Book and Author entities.

Setup

Install the Neo4j driver and Express:

npm init -y
npm install neo4j-driver expressCode language: Shell Session (shell)

Connect to the database

Since this sample uses a public database, it will connect without any changes.

Main application – app.js

const express = require('express');
const neo4j = require('neo4j-driver');

const app = express();
const PORT = 3000;

// Neo4j connection
// Create a driver instance to manage the connection to Neo4j
// The 'neo4j+s' protocol uses encrypted connections
const driver = neo4j.driver(
  'neo4j+s://demo.neo4jlabs.com',
  neo4j.auth.basic('goodreads', 'goodreads')
);

// GET /books - Get featured books from diverse authors
app.get('/books', async (req, res) => {
  // Create a new session for this request
  // Sessions are lightweight and should be created per request
  const session = driver.session();
  try {
    // Execute a Cypher query to find books by specific authors
    // The MATCH clause finds Book nodes connected to Author nodes
    // The WHERE clause filters for specific author names
    // COLLECT aggregates multiple authors into an array
    const result = await session.run(
      `MATCH (book:Book)<-[:AUTHORED]-(author:Author)
       WHERE author.name IN ['Chimamanda Ngozi Adichie', 'James Baldwin', 
              'Toni Morrison', 'Maya Angelou', 'Gabriel García Márquez']
       RETURN book.title AS title, collect(author.name) AS authors
       LIMIT 10`
    );
    
    // Transform Neo4j records into JSON objects
    // record.get() extracts values by their alias from the RETURN clause
    const books = result.records.map(record => ({
      title: record.get('title'),
      authors: record.get('authors')
    }));
    res.json(books);
  } catch (error) {
    res.status(500).json({ error: error.message });
  } finally {
    // Always close the session to release resources
    await session.close();
  }
});

// GET /books/:title - Get book by title
app.get('/books/:title', async (req, res) => {
  const session = driver.session();
  try {
    // Use parameterized queries to prevent Cypher injection
    // The $title parameter is safely substituted by the driver
    // OPTIONAL MATCH ensures we get the book even if it has no authors
    const result = await session.run(
      `MATCH (book:Book {title: $title})
       OPTIONAL MATCH (book)<-[:AUTHORED]-(author:Author)
       RETURN book.title AS title, book.isbn AS isbn, 
              collect(author.name) AS authors`,
      { title: req.params.title }  // Parameters passed as second argument
    );
    
    // Check if any records were returned
    if (result.records.length === 0) {
      return res.status(404).json({ error: 'Book not found' });
    }
    
    // Extract data from the first (and only) record
    const record = result.records[0];
    res.json({
      title: record.get('title'),
      isbn: record.get('isbn'),
      authors: record.get('authors')
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  } finally {
    await session.close();
  }
});

// GET /books/author/:name - Get books by author
app.get('/books/author/:name', async (req, res) => {
  const session = driver.session();
  try {
    // Find all books written by a specific author
    // The arrow in -[:AUTHORED]-> shows the relationship direction
    // (author)-[:AUTHORED]->(book) means author wrote the book
    const result = await session.run(
      `MATCH (author:Author {name: $name})-[:AUTHORED]->(book:Book)
       RETURN book.title AS title, book.isbn AS isbn
       LIMIT 10`,
      { name: req.params.name }
    );
    
    // Map each record to a simplified book object
    const books = result.records.map(record => ({
      title: record.get('title'),
      isbn: record.get('isbn')
    }));
    res.json(books);
  } catch (error) {
    res.status(500).json({ error: error.message });
  } finally {
    await session.close();
  }
});

// Start the Express server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
  console.log('\nTry these endpoints:');
  console.log('  http://localhost:3000/books');
  console.log('  http://localhost:3000/books/Americanah');
  console.log('  http://localhost:3000/books/author/Chimamanda%20Ngozi%20Adichie');
  console.log('  http://localhost:3000/books/author/James%20Baldwin');
});

// Cleanup on exit
// Gracefully close the driver connection when the process terminates
process.on('SIGINT', async () => {
  await driver.close();
  process.exit();
});Code language: JavaScript (javascript)

The Express framework simplifies routing with clean endpoint definitions. Each endpoint creates a session, runs a Cypher query, and returns JSON results. The queries specifically highlight diverse authors like Chimamanda Ngozi Adichie, James Baldwin, Toni Morrison, and Maya Angelou.

Key Concepts Explained

Driver and Sessions

  • Driver: A long-lived object that manages the connection pool to Neo4j. Create once per application.
  • Session: A short-lived object for executing queries. Create one per request/transaction.
  • Always close sessions in a finally block to prevent connection leaks.

Cypher Query Patterns

  • MATCH: Finds patterns in the graph
  • OPTIONAL MATCH: Like MATCH but returns null if pattern doesn’t exist (similar to LEFT JOIN)
  • WHERE: Filters results based on conditions
  • RETURN: Specifies what data to return
  • COLLECT: Aggregates values into a list

Parameterized Queries

Always use parameters (like $title, $name) instead of string concatenation to prevent Cypher injection attacks:

// Good - Safe from injection
session.run('MATCH (n:Node {name: $name})', { name: userInput })

// Bad - Vulnerable to injection
session.run(`MATCH (n:Node {name: '${userInput}'})`)Code language: JavaScript (javascript)

Error Handling

The try-catch-finally pattern ensures:

  • Errors are caught and returned as HTTP error responses
  • Sessions are always closed, even if errors occur
  • Resources are properly released

Run and test the application

Run the server:

node app.jsCode language: Shell Session (shell)

Test the endpoints:

# Get featured books from diverse authors
curl http://localhost:3000/books

# Get a specific book
curl http://localhost:3000/books/Americanah
curl "http://localhost:3000/books/Half%20of%20a%20Yellow%20Sun"

# Get books by Chimamanda Ngozi Adichie
curl "http://localhost:3000/books/author/Chimamanda%20Ngozi%20Adichie"

# Get books by James Baldwin
curl "http://localhost:3000/books/author/James%20Baldwin"

# Get books by Toni Morrison  
curl "http://localhost:3000/books/author/Toni%20Morrison"Code language: Shell Session (shell)

Or using HTTPie:

http :3000/books
http ":3000/books/Americanah"
http ":3000/books/author/Chimamanda Ngozi Adichie"
http ":3000/books/author/James Baldwin"Code language: Shell Session (shell)

Sample Response

// GET /books
[
  {
    "title": "Americanah",
    "authors": ["Chimamanda Ngozi Adichie"]
  },
  {
    "title": "Half of a Yellow Sun",
    "authors": ["Chimamanda Ngozi Adichie"]
  },
  {
    "title": "The Fire Next Time",
    "authors": ["James Baldwin"]
  }
]

// GET /books/author/Chimamanda%20Ngozi%20Adichie
[
  {
    "title": "Americanah",
    "isbn": "0307455920"
  },
  {
    "title": "Half of a Yellow Sun",
    "isbn": "1400044162"
  },
  {
    "title": "Purple Hibiscus",
    "isbn": "1616202416"
  }
]Code language: JavaScript (javascript)

Resources

Share Article