×
Community Blog Building a Simple Spring Boot API with ArangoDB on ECS

Building a Simple Spring Boot API with ArangoDB on ECS

In this article, we will show you how to build a basic Spring Boot based project on Alibaba Cloud using ArangoDB as the database engine.

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.

Overview

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 (server and client) into an Alibaba Cloud Elastic Compute Service (ECS) Ubuntu instance and experimented with the command-line and graphical tools to perform some operations on databases, collections and users. Let's consider we've that base to better follow the current write-up which will show a basic API building using ArangoDB as database engine into a Spring-Boot based project.

Prerequisites

  1. Basic Java/Spring understanding
  2. Installed ArangoDB server on Alibaba Cloud ECS
  3. Read the article "ArangoDB on Alibaba Cloud ECS"

Setting up a Spring-Boot Project

We'll be building a simple API using Java/Spring resources. Here are some important dependencies

  • ArangoDB Java driver, the official ArangoDB Java driver
  • arangodb-spring-data is the Spring Data wrapper used to easy database operations

Maven will be used as build automation to configure our project and setup the dependencies.

Initiating the Project

Let's visit https://start.spring.io to generate the structure of a Spring Boot project with the Web dependency as shown below:

1

Now we've a fresh Maven project we can unzip and open with our favorite Java IDE.

Maven

<dependency>
   <groupId>com.arangodb</groupId>
   <artifactId>arangodb-spring-data</artifactId>
   <version>3.1.0</version>
</dependency>

Spring Data ArangoDB requires at least ArangoDB 3.0 to function.

Note: ArangoDB Java driver can be used in any Java application while arango-db-spring-data is proper for those Spring projects where we want to use repositories.

Let's be sure the pom.xml have almost the following structure and/or content:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>xyz.dassiorleando</groupId>
   <artifactId>spring-arangodb</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>spring-arangodb-api</name>
   <description>Basic API: Spring Boot and ArangoDB</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.2.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>

      <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpclient</artifactId>
       <version>4.5.1</version>
      </dependency>

      <dependency>
         <groupId>com.arangodb</groupId>
         <artifactId>arangodb-spring-data</artifactId>
         <version>3.1.0</version>
      </dependency>

   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>

Create Database & Collection

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 below (owned by the current user: root):

2

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 content as main fields.

As example, here's the screen to create the Document collection article:

3

Database Configuration

Let's create our custom database configuration class ArangoDBConfiguration, it's where we'll define our database user credentials to use within the project, the database name as well as the ArangoDB host.

The configuration class is as follow:

/**
 * ArangoDB configuration class
 * To configure and provide the bean to inject later for database interactions
 * @author dassiorleando
 */
@Configuration
@EnableArangoRepositories(basePackages = { "xyz.dassiorleando.arangodb" })
public class ArangoDBConfiguration extends AbstractArangoConfiguration {

    /**
     * Database url (server + port) & credentials configs
     * @return
     */
    @Override
    public ArangoDB.Builder arango() {
        ArangoDB.Builder arango = new ArangoDB.Builder()
                .host("127.0.0.1", 8529)
                .useProtocol(Protocol.HTTP_JSON)
                .user("root")
                .password("root");
        return arango;
    }

    /**
     * Database name
     * @return
     */
    @Override
    public String database() {
        return "alibabacloudblog";
    }

}

Another option which is the most used as it's easy to identify in a large project is to use a configuration file located in the resources folder of our project, here's how our arangodb.properties file content should be:

arangodb.hosts=127.0.0.1:8529
arangodb.user=root
arangodb.password=root

Here's how to load this file so that our bean will be configured as stated:

@Override
public ArangoDB.Builder arango() {
    InputStream in = ArangoDBConfiguration.class.getResourceAsStream("arangodb.properties");
    ArangoDB.Builder arango = new ArangoDB.Builder()
            .loadProperties(in);
    return arango;
}

Note: When using ArangoDB 3.0 it is required to set the transport protocol to HTTP and fetch the dependency org.apache.httpcomponents:httpclient, here's the maven syntax to use:

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.5.1</version>
</dependency>

Article Model

Let's show how simple it's to define a model in the following case, here's the Article.java content:

/**
 * The Article model
 * @author dassiorleando
 */
@Document("article")
public class Article {

    @Id
    private String id;

    @NotNull
    private String title;

    private String content;

    private String author;

    public Article() {
    }

    public String getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Article{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

Article Service

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.

Spring Data provides default implementations for most CRUD operations which Spring Data ArangoDB is built on (we first inject the article repository):

/**
 * Article service for basic CRUD operations
 * @author dassiorleando
 */
@Service
public class ArticleService {

    @Autowired
    private ArticleRepository articleRepository;

    /**
     * To create an article
     * @param article
     * @return article
     */
    public Article save(Article article) {
        // Save the article
        return articleRepository.save(article);
    }

    /**
     * To update an article
     * @param article 
     * @return the updated article
     */
    public Article update(Article article) {
        String articleId = article.getId();
        if (articleId == null || articleId.isEmpty()) return null;

        // Update the article
        return articleRepository.save(article);
    }

    /**
     * Find a single article by its id
     * @param articleId
     * @return article
     */
    public Optional<Article> findOne(String articleId) {
        if (articleId == null || articleId.isEmpty()) return Optional.empty();
        return articleRepository.findById(articleId);
    }

    /**
     * Find all saved articles so far
     * @return
     */
    public Iterable<Article> findAll() {
        return articleRepository.findAll();
    }

    /**
     * Delete a single article by its id
     * @param articleId
     */
    public void delete(String articleId) {
        if (articleId == null || articleId.isEmpty()) return;
        articleRepository.deleteById(articleId);
    }
}

This service is used as an interface for operations across Article collections and/or further custom business logics, these functions will then be used into the Article controller.

Note that all documents created in any collections will automatically get the following server-generated attributes:

  • _id: A unique id, consisting of collection name and a server-side sequence value
  • _key: The server sequence value
  • _rev: The document's revision id

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 by just updating the Article model (an annotated POJO), 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 ArangoDB Spring Data, one might try writing plain AQL queries and end up with the same results, 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

AQL

Here are some keywords available to use in order to perform AQL queries:

  • FOR: array iteration
  • RETURN: results projection
  • FILTER: results filtering
  • SORT: result sorting
  • LIMIT: result slicing
  • LET: variable assignment
  • COLLECT: result grouping
  • INSERT: insertion of new documents
  • UPDATE: (partial) update of existing documents
  • REPLACE: replacement of existing documents
  • REMOVE: removal of existing documents
  • UPSERT: insertion or update of existing documents

When it comes to more complex queries queries using ArangoDB Query Language (AQL) can be supplied with the @Query annotation on methods in our repositories and we call the corresponding function as other.

Query example into a repository:

@Query("FOR a IN articles FILTER a.title == @0 RETURN a")
Iterable<Article> getArticleByTitle(String title);

Instead, we can only define the following repository method to perform the same query, but now with Spring Data possibilities:

Iterable<Article> findByTitle(String title);

Here's the ArticleRepository content:

/**
 * Article repository
 * @author dassiorleando
 */
public interface ArticleRepository extends ArangoRepository<Article, String> {

    // Supposing that article is the collection name to perform the query on
    @Query("FOR a IN article FILTER a.title == @0 RETURN a")
    Iterable<Article> getArticleByTitle(String title);

    Iterable<Article> findByTitle(String title);

}

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.

Article Controller

The article controller is following Spring MVC in a rest way, so that it's just dealing with incoming parameter while sending back JSON data as output:

/**
 * Article controller for CRUD operations
 * @author dassiorleando
 */
@RestController
public class ArticleResource {
    private final Logger log = LoggerFactory.getLogger(ArticleResource.class);

    private final ArticleService articleService;

    public ArticleResource(ArticleService articleService) {
        this.articleService = articleService;
    }

    /**
     * To create an article
     * @param article
     * @return
     */
    @PostMapping("/articles")
    public Article create(@RequestBody @Valid Article article) {
        log.debug("Create an article with the properties {}", article);
        return articleService.save(article);
    }

    /**
     * To update an article
     * @param article
     * @return
     */
    @PutMapping("/articles")
    public Article update(@RequestBody @Valid Article article) {
        log.debug("Update the article of title {} with the properties {}", article.getTitle(), article);
        return articleService.update(article);
    }

    /**
     * Get the list of all articles
     * @return
     */
    @GetMapping("/articles")
    public Iterable<Article> list() {
        log.debug("We just get the list of articles one more time");
        return articleService.findAll();
    }

    /**
     * Find an article by its id
     * @param id
     * @return
     */
    @GetMapping("/articles/{id}")
    public Optional<Article> findByTitle(@PathVariable @NotNull String id) {
        log.debug("Load the article of id: {}", id);
        return articleService.findOne(id);
    }

    /**
     * Delete an article by its title
     * @param id
     */
    @DeleteMapping("/articles/{id}")
    public void deleteById(@PathVariable @NotNull String id) {
        log.debug("Delete the article of id: {}", id);
        articleService.delete(id);
    }
}

Run the API

A simple command is available to run our API on our ECS or locally on any computer as simply as typing mvn spring-boot:run knowing that you've compiled (mvn clean install) your project before.

When running ArangoDB, you may encounter some issues as discussed in the following link: https://github.com/arangodb/spring-data-demo/issues/7

Conclusion

In this article, we've seen how to build a basic Spring Boot API using ArangoDB as the database management system with its ArangoDB Java Driver wrapped by Spring Data ArangoDB interface.

The full source code for this article can be found on my GitHub page.

1 1 1
Share on

Alibaba Clouder

2,603 posts | 747 followers

You may also like

Comments

Dikky Ryan Pratama May 9, 2023 at 5:45 am

if other than arangodb is it still possible?