By Dassi Orleando, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.
ArangoDB is a multi-model NoSQL database management system allowing to build high performance applications using a convenient SQL-like query language or JavaScript extensions.
Previously, we've seen how to install ArangoDB into an Alibaba Cloud Elastic Compute Service (ECS) Ubuntu server. We've also experimented the command-line and graphical tools to perform some queries on databases, collections and users. Let's consider we've that base to better follow the current write-up which will show the use of ArangoDB as database management system into a NodeJS project.
Let's assume we've done the required study or research and felt down to the fact we need to use ArangoDB to build our NodeJS App. The communication with ArangoDB server is possible via a REST interface provided many interfaces
Independently of your project architecture, there are some required npm packages we'll be using to implement a basic API:
From our project directory in the command line, let's hit the following command to install both of them while letting them persist in the package.json file:
npm install express arangojs --save
Note: for the next sections to work properly we need to run our ArangoDB server and get the correct database user's credentials to use within our API, by default there is always a root user with an empty password if not customized before.
Here is how the dashboard looks like when we're connected as a database root user:
ArangoDB's multi-model feature allows to manage many types of database with the same engine, for the sake of this article we will create and use a Document database for a basic CRUD API.
From the dashboard let's create a database called alibabacloudblog as illustrated in the screenshot bellow (owned by the current user: root):
Let's switch to be connected to the database we have just created (still from the admin panel) so that we'll manage our database to do things like create collections (represents table in relational DBMS).
We then create the article collection, it's a self-explaining word representing an article as in a blog with title and description as main fields.
As example, here's the screen to create the article Document collection:
Config file: config/db.js
Next, into the config folder we create a database configuration file called db.js within our project with the following content:
module.exports = {
'url': 'http://127.0.0.1:8529', // The default URL for a local server
'database': 'alibabacloudblog', // The database name
// Database user credentials to use
'username': 'root',
'password': 'root'
};
Database connection
Here's how we setup the database connection:
var DB = new arangojs.Database({
url: dbConfig.url
});
Let's select the database to work with, it's the one we've created up here and defined in the configuration file:
DB.useDatabase(dbConfig.database);
We specify the database user with a single line of code, it consists of the user's username and password as following:
DB.useBasicAuth(dbConfig.username, dbConfig.password);
It's possible to connect by using a bearer token with the function useBearerAuth talking the token as single parameter, read more about it here.
Note: be sure to use an actual database user you've into your server installation as for this example we're using the root user with root as its password both need to be updated in db.js file.
Next, we create the Article service. It's actually the place we'll be writing every codes necessary to manage Article while interacting with the correct database collection.
/**
* ArticleService
* @author dassiorleando
*/
var dbConfig = require('../config/db'),
arangojs = require('arangojs'),
DB = new arangojs.Database({ // Database connection
url: dbConfig.url
});
// Database selection
DB.useDatabase(dbConfig.database);
// Speficy the database user
DB.useBasicAuth(dbConfig.username, dbConfig.password);
// Collection to manage: Article
var Article = DB.collection('article');
// Save a new article with title and description as required fields
exports.create = function (article) {
if (!article.title || !article.description) return;
return Article.save(article);
};
// Update an existing article, the incoming object should have the _key field
exports.update = function (article) {
if (!article._key) return;
return Article.update(article._key, article);
};
// Remove an existing article by its _key
exports.remove = function (key) {
if (!key) return;
return Article.removeByKeys([key]);
};
// Find all articles saved so fare
exports.findAll = function () {
return Article.all();
};
// Find an article by its key
exports.findByKey = function (key) {
if (!key) return;
return Article.firstExample({_key: key});
};
This service is used as interface for Article database operations and eventual custom business logics, these functions will then be used into the resources/article.js file acting as the Article controller (Rest).
Note that all documents created in any collections will automatically get the following server-generated attributes:
Whenever you run queries on the documents in collections, don't be surprised if these additional attributes are returned as well.
At any moment of your App development you can decide to add more fields to a collection's document, as the field are dynamic there is no need to perform any other special queries on other documents unless you've something else to accomplish.
You can count the number of documents of a collections using the following query as described in the guide:
FOR doc IN collection
COLLECT WITH COUNT INTO length
RETURN length
A more populated approach to handle the whole data would have been by making a JOIN between two collections, one the Article who serve to define the blog's article model and another the user defining the article's authors. Let's read more about join here.
Instead of using the predefined collections functions we have up here in the ArangoJS SDK, one might try writing plain AQL queries and end up with the same result, here's a simple query getting a user by its Name and ID (using bind parameters):
FOR u IN users
FILTER u.id == @id && u.name == @name
RETURN u
Here are some keywords available to use in order to perform AQL queries:
FOR
: array iterationRETURN
: results projectionFILTER
: results filteringSORT
: result sortingLIMIT
: result slicingLET
: variable assignmentCOLLECT
: result groupingINSERT
: insertion of new documentsUPDATE
: (partial) update of existing documentsREPLACE
: replacement of existing documentsREMOVE
: removal of existing documentsUPSERT
: insertion or update of existing documentsLet's update a single user by its key:
INSERT {
_key: "userKeyGoesHere",
firstName: "NewFirstName",
name: "NewName",
location: "Cameroon"
} IN users
The same query performed with the UPDATE keyword is as follow:
UPDATE "userKeyGoesHere" WITH {
firstName: "NewFirstName",
name: "NewName",
location: "Beijing"
} IN users
The most important thing when performing an update is to provide the exact document _key or _id fields as they are unique, unless we'll be updating another document.
Removing a document in users collection if we know its key:
REMOVE " userKeyGoesHere " IN users
In this case as we're using the Javascript driver, here is the syntax using to perform these queries:
DB.query({
query: "query goes here",
bindVars: bindVarsObjectsIfNeeded // It's an object with the binding fields
})
.then(function(cursor) {
return cursor.next().then(function(result) {
// ...
});
})
.catch(function(err) {
// ...
});
If interested in using it for custom business logic into your API, here is the AQL getting started which is clearly showing details on how to use AQL. Queries guide with more examples are available on the reference manual.
Here's the controller action to create an Article:
router.post('/', function(req, res) {
var article = {
title: req.body.title,
description: req.body.description
};
// Explicit save
ArticleService
.create(article)
.then(function(doc) {
console.log('Saved documents ' + doc._key);
return res.status(200).json(doc);
})
.catch(function(error) {
console.error('Error saving document', error);
return res.status(500).json(error);
});
});
In the same way, the update rest action is as follow:
router.put('/', function(req, res) {
var article = {
_key: req.body._key,
title: req.body.title,
description: req.body.description
};
// Explicit update
ArticleService
.update(article)
.then(function(doc) {
console.log('Updated document ' + doc._key);
return res.status(200).json(doc);
})
.catch(function(error) {
console.error('Error updating document', error);
return res.status(500).json(error);
});
});
Let's define the find by key controller action:
router.get('/:articleKey', function(req, res) {
var articleKey = req.params.articleKey;
ArticleService
.findByKey(articleKey)
.then(function(doc) {
console.log(`Get a document by key "${req.params.articleKey}".`, doc._key);
return res.status(200).json(doc);
})
.catch(function(error) {
console.error('Error getting single document', error);
return res.status(500).json(error);
});
});
Loading all saved Articles is a simple task as it's done only by calling the all() collection's method as we did into ArticleService, here's the corresponding controller call:
router.get('/', function(req, res) {
ArticleService
.findAll()
.then(function(response) {
console.log(`Load all saved documents.`, response._result);
return res.status(200).json(response._result);
})
.catch(function(error) {
console.error('Error getting documents', error);
return res.status(500).json(error);
});
});
Deleting an Article is being done as follow:
router.delete('/:articleKey', function(req, res) {
var articleKey = req.params.articleKey;
ArticleService
.remove(articleKey)
.then(function(doc) {
if (doc.removed) console.log('Removed document' + doc);
return res.status(200).json(doc);
})
.catch(function(error) {
console.error('Error removing document', error);
return res.status(500).json(error);
});
});
We can use removeByKeys function to remove many Articles in a bulk way: Article.removeByKeys(keys) here keys is an array of Articles _key.
A basic command is available to run our API on our ECS or locally on any computer as simply as typing node server.js, if not customized by default the port 3000 will serve the App.
We can quickly use a Rest API client to test the whole thing or via the dashboard, here is the Article collection content:
In this long article, we've seen how to build a basic NodeJS API using ArangoDB as the database management system with its ArangoJS driver.
The full source code for this article can be found on my GitHub page.
Running Your Node.js Application on ECS with Systemd or Forever
2,599 posts | 762 followers
FollowAlibaba Clouder - June 5, 2019
Alibaba Clouder - June 5, 2019
Alibaba Clouder - September 7, 2020
Alibaba Clouder - June 5, 2019
Alibaba Clouder - December 7, 2018
Alibaba Clouder - October 30, 2018
2,599 posts | 762 followers
FollowElastic and secure virtual cloud servers to cater all your cloud hosting needs.
Learn MoreLearn More
An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world
Learn MoreMore Posts by Alibaba Clouder