开发者中心 » 编程语言 » Javascript » 代码指南 » 使用 Express 的 Javascript 代码演示

使用 Express 的 Javascript 代码演示

所有展示的代码均可作为一个使用 Express 的简单 Node.js 应用程序来运行。此应用程序使用了一个即开即用的演示数据库,其中包含 Goodreads 数据集,包含书籍 (Book) 和作者 (Author) 实体。

设置

安装 Neo4j 驱动程序和 Express

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

连接到数据库

由于此示例使用公共数据库,因此无需任何修改即可连接。

主应用程序 – 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 https://:${PORT}`);
  console.log('\nTry these endpoints:');
  console.log('  https://:3000/books');
  console.log('  https://:3000/books/Americanah');
  console.log('  https://:3000/books/author/Chimamanda%20Ngozi%20Adichie');
  console.log('  https://: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)

Express 框架通过简洁的端点定义简化了路由。每个端点都会创建一个会话 (session)、运行 Cypher 查询并返回 JSON 结果。这些查询特别重点展示了诸如 Chimamanda Ngozi Adichie、James Baldwin、Toni Morrison 和 Maya Angelou 等多元化作者。

关键概念解释

驱动程序与会话

  • 驱动程序 (Driver):一个长期存在的对象,用于管理到 Neo4j 的连接池。每个应用程序只需创建一个。
  • 会话 (Session):一个短期存在的对象,用于执行查询。每个请求/事务创建一个。
  • 务必在 finally 块中关闭会话,以防止连接泄漏。

Cypher 查询模式

  • MATCH:在图中查找模式
  • OPTIONAL MATCH:类似于 MATCH,但如果模式不存在则返回 null(类似于 LEFT JOIN)
  • WHERE:根据条件过滤结果
  • RETURN:指定要返回的数据
  • COLLECT:将值聚合为列表

参数化查询

始终使用参数(如 $title、$name)代替字符串拼接,以防止 Cypher 注入攻击

// 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)

错误处理

try-catch-finally 模式确保:

  • 错误被捕获并作为 HTTP 错误响应返回
  • 即使发生错误,会话也会被关闭
  • 资源得到妥善释放

运行并测试应用程序

运行服务器

node app.jsCode language: Shell Session (shell)

测试端点

# Get featured books from diverse authors
curl https://:3000/books

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

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

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

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

或使用 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)

响应示例

// 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)

资源

分享文章