Building Graph Database Applications with Apache AGE and Node.js: A Step-by-Step Tutorial
Omar Saad
Posted on June 22, 2023
In this tutorial we will introduce how to use Apache AGE with your NodeJs application.
What is Apache AGE ?
Apache AGE is a graph database system that is built on top of PostgreSQL. It allows users to store, manage, and analyze large-scale graph data in a highly performant and scalable way.
Apache AGE combines the benefits of a powerful relational database management system (PostgreSQL) with the flexibility and scalability of a graph database. This means that users can store their data in a relational format and also perform graph-based queries and analyses on the data.
Prerequisites
PostgreSQL Version 11 or 12: Make sure you have PostgreSQL installed on your machine. Apache AGE relies on PostgreSQL, so it's important to have either version 11 or 12.
Apache AGE Installation and Configuration: Install and configure Apache AGE to seamlessly integrate with your PostgreSQL installation. If you haven't installed PostgreSQL and Apache AGE yet, we recommend following our comprehensive step-by-step guide for easy installation on Windows. You can access the tutorial here. This guide will walk you through the entire process and ensure a smooth setup.
Node.js: Ensure that Node.js is installed on your system. You can download and install the latest version of Node.js from the official website: Node.js
Having Node.js installed will provide the necessary runtime environment for running your Node.js applications and executing JavaScript code.
Verify the installation by opening a terminal or command prompt and running the following command:
node --version
You should see the version number of Node.js printed in the terminal if it is installed correctly.
If Node.js is not installed, please download and install it using the appropriate installer for your operating system.
Now that you have Node.js installed, you're ready to proceed with the tutorial and explore the integration of Apache AGE with Node.js and PostgreSQL.
Apache AGE with NodeJs
1. To initialize a new Node.js project, you can run the npm init
command in your project directory. This command will prompt you with a series of questions to gather information about your project and generate a package.json
file. The package.json
file is essential for managing your project's dependencies and scripts.
To run npm init
, open your terminal or command prompt, navigate to your project directory, and execute the following command:
npm init
You will be prompted to enter various details such as the project name, version, description, entry point, test command, repository, and more. Feel free to provide the relevant information according to your project requirements. If you wish to skip any prompts, you can add the -y
flag to automatically generate the package.json
file with default values:
npm init -y
Once you have completed the npm init
process, a package.json
file will be created in your project directory, which will serve as a configuration file for your Node.js project.
2.To configure your application's environment variables, you'll need to create a .env
file. This file will store the necessary variables for your application to connect to the PostgreSQL database. Here's an example of how the .env
file should be structured:
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_DB=your_db_name
POSTGRES_USER=your_user_name
POSTGRES_PASSWORD=your_password
Replace your_db_name
, your_user_name
, and your_password
with the actual values specific to your PostgreSQL setup. These variables specify the host, port, database name, username, and password required for establishing a connection between your Node.js application and the PostgreSQL database.
3.Include the following dependencies in package.json
file.
"express": "^4.17.1",
"pg": "^8.5.1",
"dotenv": "^8.2.0"
Ensure that the formatting of your package.json
file remains intact.
Once you have added the dependencies, save the changes to your package.json
file. Your Node.js application will now be able to utilize these dependencies.
4. Create server.js
file in your main directory. This file will be the entry point of your application.
const express = require('express');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 3000;
// Define your routes and middleware here
//TODO
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
5. Create a file named database.js
in your project's directory. This file will handle the database connection settings.
import { Pool } from 'pg';
import dotenv from 'dotenv';
dotenv.config();
const POSTGRES_HOST = process.env.POSTGRES_HOST;
const POSTGRES_PORT = process.env.POSTGRES_PORT;
const POSTGRES_DB = process.env.POSTGRES_DB;
const POSTGRES_USER = process.env.POSTGRES_USER;
const POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD;
const client = new Pool({
host: POSTGRES_HOST,
port: POSTGRES_PORT,
database: POSTGRES_DB,
user: POSTGRES_USER,
password: POSTGRES_PASSWORD,
// If local, set ssl to false to avoid errors
// ssl: true,
});
async function initAGE() {
const sql = `CREATE EXTENSION IF NOT EXISTS age;
LOAD 'age';
client.query(sql);
SET search_path to ag_catalog,"$user",public; `;
try{
const conn = await client.connect();
await conn.query(sql);
conn.release();
} catch (err) {
throw new Error(`Could not init agent. Error: ${err}`);
}
}
export default {
initAGE,
client
}
6. Create product_model.js
file in which we will implement the basic CRUD operations for our products application.
import client from "./database";
export class ProductStore {
GRAPH_NAME = 'products';
async getAllProducts() {
try {
const conn = await client.connect();
const sql = `SELECT * FROM
cypher('${GRAPH_NAME}', $$
MATCH (p:Product) RETURN p
$$)
as (result agtype)`;
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Could not get products. Error: ${err}`);
}
}
async createProduct(name,price,category) {
try{
const conn = await client.connect();
const sql = `SELECT * FROM
cypher('${GRAPH_NAME}', $$
CREATE (p:Product {name: '${name}', price: ${price}, category: '${category}'}) RETURN p
$$)
as (result agtype)`;
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Could not add new product ${product.name}. Error: ${err}`);
}
}
async getProductById(id) {
try {
const conn = await client.connect();
const sql = `SELECT * FROM
cypher('${GRAPH_NAME}', $$
MATCH (p:Product) WHERE id(p) = ${id} RETURN p
$$)
as (result agtype)`;
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Could not get product ${id}. Error: ${err}`);
}
}
async updateProduct(id, name, price, category) {
try {
const conn = await client.connect();
const sql = `SELECT * FROM
cypher('${GRAPH_NAME}', $$
MATCH (p:Product) WHERE id(p) = ${id} SET p.name = '${name}', p.price = ${price}, p.category = '${category}' RETURN p
$$)
as (result agtype)`;
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Could not update product ${id}. Error: ${err}`);
}
}
async deleteProduct(id) {
try {
const conn = await client.connect();
const sql = `SELECT * FROM
cypher('${GRAPH_NAME}', $$
MATCH (p:Product) WHERE id(p) = ${id} DELETE p
$$)
as (result agtype)`;
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Could not delete product ${id}. Error: ${err}`);
}
}
}
7. We will edit our server.js
file to add products routes.
For this example we will only implement GET and POST routes.
The updated server.js
file:
const express = require('express');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 3000;
// Define your routes and middleware here
const initAGE = require('./database').initAGE;
// Load AGE extension
initAGE();
app.get('/products', (req, res) => {
try{
const productModel = require('./product_model');
const products = productModel.getProducts();
res.send({
message : 'Products retrieved successfully',
data : products
});
return;
} catch (err) {
res.send({
message : `Could not get products. Error: ${err}`,
data : []
});
return;
}
});
app.post('/products', (req, res) => {
try{
const productModel = require('./product_model');
const products = productModel.createProduct(req.body.name, req.body.price, req.body.category);
res.send({
message : 'Products created successfully',
data : products
});
return;
} catch (err) {
res.send({
message : `Could not create products. Error: ${err}`,
data : []
});
return;
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
8. Run npm install
to install the project dependencies.
9. Run npm run start
to start nodejs server.
10. Now you can test the server using postman or any other tools by making the following requests:
POST localhost:3000/products
The body of the POST request should be in the following format:
"name" : "Iphone 14",
"price": 1000,
"category" : "Mobile phones"
If we want to retrieve all the products than we should send the following request:
GET localhost:3000/products
This request should return all the products in our database.
Congratulations on successfully building your Node.js application integrated with PostgreSQL and Apache AGE! You've created a powerful combination that enables efficient data management and advanced graph-based querying. Well done!
In the upcoming tutorials, we will delve into building more complex applications to further deepen our understanding. We will explore advanced features, demonstrate practical use cases, and tackle more intricate scenarios. By expanding our knowledge and exploring these advanced concepts, we will be able to leverage the full potential of integrating Apache AGE.
References
Apache AGE documentation
Apache AGE Github
Contribute to Apache AGE
Posted on June 22, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
June 22, 2023