In the previous article, we worked on creating a new Spring Boot service, setting up MongoDB using Docker, and connecting the Spring Boot application to the MongoDB database.
In this article, we'll focus on setting up a basic GET endpoint in our Spring Boot application to read data from the MongoDB instance running on our local machine.
What you'll need
- Intellij IDE - for working with code implementation
- Postman - for making http calls and testing the endpoint
Creating the entity models
In order to work with MongoDB, we need to create a POJO to represent the data in the database. Each property of the class will represent a field in our document.
In the Spring Boot project, create a new directory under src/com.${project_name}/domain/student
. Then, create a Student Java class and add the following code to it:
@Data
@Document // We implement this annotation to tell mongodb that this class represent a single document in our collection.
public class Student {
@Id // Every document must have their own id, that is why we create am id property on the class.
private String id;
private String firstName;
private String lastName;
@Indexed(unique = true)
private String email; // We tell mongodb that this field must be unique. This will be one of our business rules on this project.
private Gender gender;
private Address address;
private List<String> favouriteSubjects;
private BigDecimal totalSpentInBooks;
private LocalDateTime created;
// Finally, we define a constructor for the class.
public Student(String firstName, String lastName, String email, Gender gender, Address address, List<String> favouriteSubjects, BigDecimal totalSpentInBooks, LocalDateTime created) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.address = address;
this.favouriteSubjects = favouriteSubjects;
this.totalSpentInBooks = totalSpentInBooks;
this.created = created;
}
⚠️ Note: One of our business rule for this project is that every student must have a unique email address.
You can notice that we have Address
and Gender
classes that we haven't defined/created yet. So let's go ahead and create them.
// The contents of Address.java. Please add the contents accordingly.
@Data // This lombok annotation allow us to have getters and setters for each properties of the class. Lombok manages this for us so we don't have to write boiletplate code.
@AllArgsConstructor // This lombok annotation allows us to create a new instance of the Address object without passing any arguments.
public class Address {
private String country;
private String city;
private String postCode;
}
// The contents of Gender.java.
public enum Gender {
MALE, FEMALE
}
Create separate files for each Java class. It is not best practice to combine different Java objects/classes that have entirely different contexts.
Once the two classes have been created, we can resolve the error appearing in the Student
class.
MVC and implementation
Spring MVC is a powerful framework for building web applications in Java. It follows the Model-View-Controller design pattern, which separates the application logic into three interconnected components: the model (data), the view (user interface), and the controller (business logic). This separation allows for more organized and maintainable code.
For more information, you can explore the official documentation:
Repository
Create a new Java interface under the directory src/com.${project_name}/student
and name it StudentRepository
. Implement the following code in the newly created interface.
public interface StudentRepository extends MongoRepository<Student, String> {
Optional<Student> findStudentByEmail(String email); // Here, we are implementing a method that would allow us to fetch a student data that matches the email that we provide as parameter.
}
We are leveraging the Mongo repository to allow us to interact with the database.
MongoRepository
is an interface provided by Spring Data in the package org.springframework.data.mongodb.repository.MongoRepository
extends the PagingAndSortingRepository
and QueryByExampleExecutor
interfaces that further extend the CrudRepository
interface. MongoRepository
provides all the necessary methods which help to create a CRUD application and it also supports the custom derived query methods.
Service
We define the business logic of our application in the service classes. The service class acts as a bridge between the controller and repository classes. This is a best practice design when developing Spring Boot applications. The controller must never interact directly with the database implementation, which is the repository in this case.
Create a new class at the same level as the StudentController
and name it StudentService
. Then, add the following implementation.
@Service // Service classess need to be annotated with this in order for spring boot to determine that this a service class.
@AllArgsConstructor // Since we are requireing an instance of the StudentRepository class here, we are levering lombok to do the injection for us via constructor.
public class StudentService {
private final StudentRepository studentRepository;
public List<Student> getAllStudents() { // This method will be the one that is accessed by the controller so that i can fetch data from the mongodb.
return studentRepository.findAll(); // with this implementation, we are following the best practice of not having the controller class directly call the database implementation (repository).
}
}
Controller
In Spring MVC, we define the endpoints of our application in the controller.
Create a StudentController
Java class at the same level as the Student
class. Then, implement the following code.
@RestController // All controller classes need to be annotated with this, so that spring will know that there are endpoints on this controller class.
@AllArgsConstructor // We are injecting the StudentService instance into this class, and we are leveraging lombok's annotation to do this for us instead of having to implement a constructor and then annotate it with @Autowired annotation.
public class StudentController {
private final StudentService studentService; // Inject the instance of StudentService
@GetMapping // This mapping means that we are mapping this endpoint to a GET HTTP request.
@RequestMapping("api/v1/students") // Here is where we difine the endpoint URI.
public List<Student> fetchAllStudents() {
return studentService.getAllStudents();
}
}
Putting it all together
Implementing code to populate the database
For this tutorial, we need to pre-populate the data in our local MongoDB so that we have data to test and fetch.
Open the class of your application with the main
function and add this code implementation under the same class.
/*
* @info: Succeeding application runs will throw and error when `auto-index-creation` is enabled
* */
@Bean
CommandLineRunner runner(StudentRepository studentRepository, MongoTemplate mongoTemplate) {
return args -> {
Address address = new Address("England", "London", "NE9");
Student student = new Student("Jamila", "Ahmed", "jahmed@gmail.com", Gender.FEMALE, address, List.of("Computer Science"), BigDecimal.TEN, LocalDateTime.now());
/*
* @note: We implement a find criteria to search for existing students on the database.
* It should only insert new student if no other students are found with the same email.
*
*/
scanAndInsertInitialDataUsingRepository(studentRepository, student);
};
}
private static void scanAndInsertInitialDataUsingRepository(StudentRepository studentRepository, Student student) {
studentRepository.findStudentByEmail(student.getEmail()).ifPresentOrElse(success -> {
System.out.println(success + " already exists.");
}, () -> {
System.out.println("Inserting student " + student);
studentRepository.insert(student);
});
}
The code implementation above does the following:
- Builds a new
Student
object with the provided details. - Interacts with MongoDB to check if there's an existing user with the specified email address. Remember our business rule for this tutorial: "every student must have a unique email address."
- If there is no student with the specified email address, the new student object is persisted to MongoDB. Otherwise, a log message is printed, and no further action is taken."
Testing our application
What it looks like
When you open the mongo-express app in your browser, you are actually interfacing with the MongoDB database and viewing the data directly. With Express, you can also make edits to the contents of MongoDB. This means you can edit, create, and delete documents as well as collections.
When you make an HTTP request with Postman to the Spring Boot app, you are not interfacing directly with MongoDB; instead, the Spring Boot app only exposes certain functionalities that are allowed for the client. It does this via endpoints. Each CRUD operation will have an individual endpoint that allows the client to perform specific functionalities. What we have created is a REST API that exposes the functionality of fetching all the students from MongoDB.
Disclaimer: There's a whole field of knowledge that deals with RESTful API designs, and I admit that our GET endpoint needs some improvements. But for the sake of simplicity, we'll just implement the specification to allow us to get all the student data from the database.
Start the application
Open your terminal and run the MongoDB and application with the following commands:
make start-db
make start-app
⚠️ Note: Wait for the MongoDB database to start successfully before running the spring boot app. Or else error messages and issues will show.
Performing the postman request
Open postman and create a new request with the following details:
Request-Type: GET
URI: http://localhost:8080/api/v1/students
ℹ️ Info: Make sure that the application and database are successfully running before making the postman request.
You can also perform a curl request directly from your terminal as such:
curl --location 'http://localhost:8080/api/v1/students'
Conclusion
In this article, we continued our work by implementing the entity models and discussing common best practices for implementing Spring Boot MVC. We added implementation code to pre-populate our MongoDB database and successfully created a simple GET endpoint to fetch that data through an HTTP call to our Spring Boot application. Congratulations! By now, you have an understanding of how to build a simple Spring Boot backend service that leverages MongoDB as the database source.
I plan to create a new article to develop the remaining endpoints to achieve a complete CRUD operation for our backend service. This means creating additional endpoints to perform the other CRUD functionalities. Here is the GitHub repository for this Spring Boot project, where you can follow the configurations and implementations discussed in this tutorial.
I hope you learned something new from this small tutorial series.
Top comments (0)