Using MongoDB and Spring Boot to Create a RESTful Web Service

Robert Rice Java, Spring Boot, Technology Snapshot Leave a Comment

Spring Boot is a framework designed to simplify the bootstrapping and development of a new Spring application. The framework takes an opinionated approach to configuration, freeing developers from the need to define a boilerplate configuration. MongoDB is a simple set up and easy to use document database. A RESTful API is an application program interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data.

In this post, I will demonstrate the process of creating a RESTful web application with Spring Boot and MongoDB.

First, Create a Database:

If you need to set up an instance of MongoDB, you can find that information here. We can start by creating a test database, which we will call Megaman. You can do this via command line in the MongoDB shell.

use megaman;

Add a MongoDB Collection and Populate with Data:

A “collection” in MongoDB is basically what you would refer to as a “table” in relational databases. Once again, using the command line, we create a collection to contain our test data.

db.createCollection(“bosses”);

db.bosses.insertMany([{“
  name”: “Man”,
  “weapon”: “Rolling Cutter”,
  “weakness”: “Super Arm”
 },
 {“
  name”: “Guts Man”,
  “weapon”: “Super Arm”,
  “weakness”: “Hyper Bomb”
 },
 {“
  name”: “Fire Man”,
  “weapon”: “Fire Wave”,
  “weakness”: “Ice Slasher”
 }
]);

If you then query the new collection with db.bosses.find({});, you will see that MongoDB automatically creates and populates an identity field for each record.

Using Spring Initializr to Create a Project

Spring Initializr can lay a lot of the groundwork for your Spring Boot project. Enter the following information at start.spring.io:

Group: The package name
Example: com.example.rrice

Artifact: The project name
Example: Megaman

Dependencies: These are the features that will be added to your project (remember, you can always add these later).
Example: Our tutorial will use the Web and MongoDB dependencies.

Then click “Generate Project” to download a .zip file with the basic project structure.

Adding Model to Spring Boot Project

Unzip the downloaded project and open it in your IDE. We’ll need to add a model to represent the data you created for your MongoDB collection. Create a new java class com.example.rrice.megaman.models.Bosses, as follows:

package com.example.rrice.megaman.models;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
public class Bosses {
 @Id
 public ObjectId _id;
 public String name;
 public String weapon;
 public String weakness;

 // Constructors
 public Bosses() {}
 public Bosses(ObjectId _id, String name, String weapon, String weakness) {
  this._id = _id;
  this.name = name;
  this.weapon = weapon;
  this.weakness = weakness;
 }

 // ObjectId needs to be converted to string
 public String get_id() {
  return _id.toHexString();
 }
 public void set_id(ObjectId _id) {
  this._id = _id;
 }

 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

 public String getWeapon() {
  return weapon;
 }
 public void setWeapon(String weapon) {
  this.weapon = weapon;
 }

 public String getWeakness() {
  return weakness;
 }
 public void setWeakness(String weakness) {
  this.weakness = weakness;
 }
}

Adding Repository to Spring Boot Project

We now need to establish a connection between the model and MongoDB, via a repository interface.

See Also:  Unit Testing Your Architecture With ArchUnit

Create a new class, com.example.rrice.megaman.repositories.BossesRepository.java, and extend it from the MongoRepository class.

The class must be named < object>Repository.java (where object is the DO class name you created earlier) in this way so that MongoDB can establish the relationship.

This superclass already contains generic methods like save and delete, but there are additional methods we will need to implement ourselves. We do not, however, need to manually implement query methods. If we use Spring Boot’s repository naming conventions, the MongoRepository will intelligently construct the queries at runtime. This means that our interface is simplified, in the following way:

package com.example.rrice.megaman.repositories;
import com.example.rrice.megaman.models.Bosses;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface BossesRepository extends MongoRepository < Bosses, String > {
 Bosses findBy_id(ObjectId _id);
}

Adding the MongoDB Connection Info

In the src/main/resources/application.properties file, add the following lines to establish the connection details needed by MongoDB, replacing the information in brackets with the information specific to your MongoDB instance:

spring.data.mongodb.host=[host]
spring.data.mongodb.port=[port]
spring.data.mongodb.authentication-database=[authentication_database]
spring.data.mongodb.username=[username]
spring.data.mongodb.password=[password]
spring.data.mongodb.database=megaman

Creating REST Controller

Spring now has everything it needs to connect to MongoDB. We now need to establish endpoints that we can contact in order to interact with the database. We will do this in a Spring Rest Controller, using Request Mappings in order to map requests with functions.

Add a controller class called com.example.rrice.megaman.BossesController.java. The file will be laid out as follows:

package com.example.rrice.megaman;
import com.example.rrice.megaman.models.Bosses;
import com.example.rrice.megaman.repositories.BossesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping(“/bosses”)
  public class BossesController {
   @Autowired
   private BossesRepository repository;
  }

The @RestController annotation tells Spring that this class will be requested by URL and will return data to the requester. The @RequestMapping annotation specifies the base URL that the controller will be handling, so any request to the host starting with “/bosses” will be directed to this controller. The @Autowired annotation creates an instance of the BossesRepository object that will allow us to access and modify the bosses database.

Add the REST Endpoints

Add the following endpoints to BossesController.java.

GET
@RequestMapping(value = “/”, method = RequestMethod.GET)
  public List getAllBosses() {
   return repository.findAll();
  }
  @RequestMapping(value = “/{id}”, method = RequestMethod.GET)
   public Bosses getBossById(@PathVariable(“id”) ObjectId id) {
    return repository.findBy_id(id);
   }

The first mapping takes any GET requests to the host with a URL of /bosses/ and maps them to the getAllBosses() method, which requests all documents from the bosses collection.

The second mapping takes any GET requests to the host with a URL of /bosses/ followed by an ObjectId and maps them to the getBossById() method. This searches the bosses collection for the document with an _id field equal to the ObjectId in the URL.

PUT
@RequestMapping(value = “/{id}”, method = RequestMethod.PUT)
  public void modifyBossById(@PathVariable(“id”) ObjectId id, @Valid @RequestBody Bosses bosses) {
   bosses.set_id(id);
   repository.save(bosses);
  }

This mapping expects a request body (in JSON format) with each of the fields that a Bosses object contains (name, weapon, and weakness). The ID in the request URL is the _id of the document to be modified.

See Also:  Conditionally Disabling and Filtering Tests in JUnit 5
POST
@RequestMapping(value = “/”, method = RequestMethod.POST)
  public Bosses createBoss(@Valid @RequestBody Bosses bosses) {
   bosses.set_id(ObjectId.get());
   repository.save(bosses);
   return bosses;
  }

This mapping expects a request body (in JSON format) with each of the fields that a Bosses object contains (name, weapon, and weakness), and assigns it a new ObjectId. This object is then inserted into the bosses collection, and the new Bosses object is returned.

DELETE
@RequestMapping(value = “/{id}”, method = RequestMethod.DELETE)
  public void deleteBoss(@PathVariable ObjectId id) {
   repository.delete(repository.findBy_id(id));
  }

This endpoint takes the _id of a document in the bosses collection and removes that document from the collection.

Testing Your API

Now that the controller has all of our endpoints, we can begin testing our API!

From the command line, in the project root, run the mvn spring-boot:run command to compile the code and start the Spring server with the default port 8080.

Once the server starts, you are free to test your API however you choose.

POST ‘http://localhost:8080/bosses’

With Body:

{
 “name”: “Bomb Man”,
 “weapon”: “Hyper Bomb”,
 “weakness”: “Fire Storm”
}

And Header:

 Content - Type: application / json

Returns:

{
 “_id”: “5 aecef5b6d55754834124df3”,
 “name”: “Bomb Man”,
 “weapon”: “Hyper Bomb”,
 “weakness”: “Fire Storm”
}
PUT ‘http://localhost:8080/bosses/5aecef5b6d55754834124df3

With Body:

{
 “name”: “Bomb Man”,
 “weapon”: “Hyper Bomb”,
 “weakness”: “Rolling Cutter”
}

And Header:

 Content - Type: application / json

Returns:

empty response
GET ‘http://localhost:8080/bosses/5aecef5b6d55754834124df3’

Returns:

{
 “_id”: “5 aecef5b6d55754834124df3”,
 “name”: “Bomb Man”,
 “weapon”: “Hyper Bomb”,
 “weakness”: “Rolling Cutter”
}
DELETE ‘http://localhost:8080/bosses/5aecef5b6d55754834124df3’

Returns:

empty response
GET ‘http://localhost:8080/bosses’

Returns:

[{
  “_id”: “5 aeccb0a18365ba07414356c”,
  “name”: “Cut Man”,
  “weapon”: “Rolling Cutter”,
  “weakness”: “Super Arm”
 },
 {
  “_id”: “5 aeccb0a18365ba07414356d”,
  “name”: “Guts Man”,
  “weapon”: “Super Arm”,
  “weakness”: “Hyper Bomb”
 },
 {
  “_id”: “5 aeccb0a18365ba07414356e”,
  “name”: “Fire Man”,
  “weapon”: “Fire Wave”,
  “weakness”: “Ice Slasher”
 }
]

Final Thoughts

This is a fairly simple and straightforward implementation of these technologies, but it should give a good idea about more complicated implementations that can be facilitated by using them. You can also establish parent/child relationships and query them with objects backed by MongoDB data. You can also create more complex queries using MongoDB template. We will explore these in the next blog entry.

What Do You Think?