In this tutorial, we’re gonna build a Spring Boot Rest API example that use Spring Data MongoDB & Maven to make CRUD operations with MongoDB database. You’ll know:
- How to configure Spring Data to work with MongoDB Database
- How to define MongoDB Data Models and Repository interfaces
- Way to create Spring Rest Controller to process HTTP requests
- Way to use Spring Data MongoDB to interact with MongoDB Database
More Practice:
– Spring Boot, MongoDB, Reactive CRUD example
– Spring Boot + GraphQL + MongoDB example with Spring Data & graphql-java
– Spring Boot, MongoDB: JWT Authentication with Spring Security
– Spring Boot MongoDB Pagination example with Spring Data
Exception Handling:
– Spring Boot @ControllerAdvice & @ExceptionHandler example
– @RestControllerAdvice example in Spring Boot
Unit Test: Spring Boot Unit Test for Rest Controller
Documentation: Spring Boot Swagger 3 example
Caching: Spring Boot Redis Cache example
Validation: Spring Boot Validate Request Body
Fullstack:
– Angular 8 + Spring Boot + MongoDB example
– Angular 10 + Spring Boot + MongoDB example
– Angular 11 + Spring Boot + MongoDB example
– Angular 12 + Spring Boot + MongoDB example
– Angular 13 + Spring Boot + MongoDB example
– Angular 14 + Spring Boot + MongoDB example
– Angular 15 + Spring Boot + MongoDB example
– React + Spring Boot + MongoDB example
– Vue.js + Spring Boot + MongoDB example
Contents
Overview of Spring Boot MongoDB CRUD example
We will build a Spring Boot MongoDB Rest CRUD API for a Tutorial application in that:
- Each Tutorial has id, title, description, published status.
- Apis help to create, retrieve, update, delete Tutorials.
- Apis also support custom finder methods such as find by published status or by title.
These are APIs that we need to provide:
Methods | Urls | Actions |
---|---|---|
POST | /api/tutorials | create new Tutorial |
GET | /api/tutorials | retrieve all Tutorials |
GET | /api/tutorials/:id | retrieve a Tutorial by :id |
PUT | /api/tutorials/:id | update a Tutorial by :id |
DELETE | /api/tutorials/:id | delete a Tutorial by :id |
DELETE | /api/tutorials | delete all Tutorials |
GET | /api/tutorials/published | find all published Tutorials |
GET | /api/tutorials?title=[keyword] | find all Tutorials which title contains keyword |
– We make CRUD operations & finder methods using Spring Data MongoDB.
– Rest Controller will be created with the help of Spring Web MVC.
Technology
- Java 17 / 11 / 8
- Spring Boot 3 / 2 (with Spring Web MVC, Spring Data MongoDB)
- MongoDB
- Maven
Project Structure
Tutorial
data model class corresponds to entity and table tutorials.TutorialRepository
is an interface that extends MongoRepository for CRUD methods and custom finder methods. It will be autowired inTutorialController
.TutorialController
is a RestController which has request mapping methods for RESTful requests such as: getAllTutorials, createTutorial, updateTutorial, deleteTutorial, findByPublished…- Configuration for Spring Data MongoDB is in application.properties.
- pom.xml contains dependencies for Spring Boot Web MVC and Spring Data MongoDB.
Let’s implement this application right now.
Create & Setup Spring Boot project
Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.
Then open pom.xml and add these dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
Configure Spring Data MongoDB
Under src/main/resources folder, open application.properties and add following lines.
spring.data.mongodb.database=bezkoder_db
spring.data.mongodb.port=27017
Define Data Model
Our Data model is Tutorial with four fields: id, title, description, published.
In model package, we define Tutorial
class.
model/Tutorial.java
package com.bezkoder.spring.data.mongodb.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "tutorials")
public class Tutorial {
@Id
private String id;
private String title;
private String description;
private boolean published;
public Tutorial() {
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean isPublished) {
this.published = isPublished;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
@Document
annotation helps us override the collection name by “tutorials”.
Create Repository Interface
Let’s create a repository to interact with Tutorials from the database.
In repository package, create TutorialRepository
interface that extends MongoRepository
.
repository/TutorialRepository.java
package com.bezkoder.spring.data.mongodb.repository;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.bezkoder.spring.data.mongodb.model.Tutorial;
public interface TutorialRepository extends MongoRepository<Tutorial, String> {
List<Tutorial> findByTitleContaining(String title);
List<Tutorial> findByPublished(boolean published);
}
Now we can use MongoRepository’s methods: save()
, findOne()
, findById()
, findAll()
, count()
, delete()
, deleteById()
… without implementing these methods.
We also define custom finder methods:
– findByTitleContaining()
: returns all Tutorials which title contains input title
.
– findByPublished()
: returns all Tutorials with published
having value as input published
.
The implementation is plugged in by Spring Data MongoDB automatically.
Create Spring Rest APIs Controller
Finally, we create a controller that provides APIs for creating, retrieving, updating, deleting and finding Tutorials.
controller/TutorialController.java
package com.bezkoder.spring.data.mongodb.controller;
...
import com.bezkoder.spring.data.mongodb.model.Tutorial;
import com.bezkoder.spring.data.mongodb.repository.TutorialRepository;
@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {
@Autowired
TutorialRepository tutorialRepository;
@GetMapping("/tutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
}
@GetMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") String id) {
}
@PostMapping("/tutorials")
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
}
@PutMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") String id, @RequestBody Tutorial tutorial) {
}
@DeleteMapping("/tutorials/{id}")
public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") String id) {
}
@DeleteMapping("/tutorials")
public ResponseEntity<HttpStatus> deleteAllTutorials() {
}
@GetMapping("/tutorials/published")
public ResponseEntity<List<Tutorial>> findByPublished() {
}
}
– @CrossOrigin
is for configuring allowed origins.
– @RestController
annotation is used to define a controller and to indicate that the return value of the methods should be be bound to the web response body.
– @RequestMapping("/api")
declares that all Apis’ url in the controller will start with /api
.
– We use @Autowired
to inject TutorialRepository
bean to local variable.
Now I will show you how to implement each controller’s CRUD methods.
Create Operation
We use @PostMapping
annotation for handling POST HTTP requests.
A new Tutorial will be created by MongoRepository.save()
method.
@PostMapping("/tutorials")
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
try {
Tutorial _tutorial = tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Retrieve Operations
We use @GetMapping
annotation for handling GET HTTP requests, then Repository’s findAll()
, findByTitleContaining(title)
, findByPublished()
method to get the result.
getAllTutorials()
: returns List of Tutorials, if there istitle
parameter, it returns a List in that each Tutorial contains the titlegetTutorialById()
: returns Tutorial by givenid
findByPublished()
: return published Tutorials
@GetMapping("/tutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
try {
List<Tutorial> tutorials = new ArrayList<Tutorial>();
if (title == null)
tutorialRepository.findAll().forEach(tutorials::add);
else
tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") String id) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@GetMapping("/tutorials/published")
public ResponseEntity<List<Tutorial>> findByPublished() {
try {
List<Tutorial> tutorials = tutorialRepository.findByPublished(true);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Update Operation
@PutMapping
will help us handle PUT HTTP requests.
– updateTutorial()
receives id
and a Tutorial payload.
– from the id
, we get the Tutorial from database using findById()
method.
– then we use the payload and save()
method for updating the Tutorial.
@PutMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") String id, @RequestBody Tutorial tutorial) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
Tutorial _tutorial = tutorialData.get();
_tutorial.setTitle(tutorial.getTitle());
_tutorial.setDescription(tutorial.getDescription());
_tutorial.setPublished(tutorial.isPublished());
return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Delete Operation
We use @DeleteMapping
for DELETE HTTP requests.
There are 2 methods:
deleteTutorial()
: delete a Tutorial document with givenid
deleteAllTutorials()
: remove all documents in tutorials collection
The operations is done with the help of MongoRepository’s deleteById()
and deleteAll()
method.
@DeleteMapping("/tutorials/{id}")
public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") String id) {
try {
tutorialRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/tutorials")
public ResponseEntity<HttpStatus> deleteAllTutorials() {
try {
tutorialRepository.deleteAll();
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Run & Test
Run Spring Boot application with Maven command: mvn spring-boot:run
.
Create some Tutorials:
Check MongoDB database:
Update some Tutorials:
Check MongoDB database after updating:
Get all Tutorials:
Get a Tutorial by Id:
Find all published Tutorials:
Find all Tutorials which title contains ‘ring’:
Delete a Tutorial:
Check MongoDB database after deleting the Tutorial:
Delete all Tutorials:
You can use the Simple HTTP Client using Axios to check it.
Or: Simple HTTP Client using Fetch API
Conclusion
Today we’ve built a Rest CRUD API using Spring Boot, Spring Data MongoDB & Maven to create, retrieve, update, delete documents in MongoDB database.
We also see that MongoRepository
supports a great way to make CRUD operations and custom finder methods without need of boilerplate code.
This project can be used in the following full-stack:
– Angular 8 + Spring Boot + MongoDB example
– Angular 10 + Spring Boot + MongoDB example
– Angular 11 + Spring Boot + MongoDB example
– Angular 12 + Spring Boot + MongoDB example
– Angular 13 + Spring Boot + MongoDB example
– Angular 14 + Spring Boot + MongoDB example
– Angular 15 + Spring Boot + MongoDB example
– React + Spring Boot + MongoDB example
– Vue.js + Spring Boot + MongoDB example
You may need to handle Exception with:
– Spring Boot @ControllerAdvice & @ExceptionHandler example
– @RestControllerAdvice example in Spring Boot
Or make pagination with structure of the response like this:
{
"totalItems": 12,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
With the tutorial: Spring Boot MongoDB Pagination example with Spring Data
Happy learning! See you again.
Further Reading
- Spring Boot, MongoDB: JWT Authentication with Spring Security
- Spring Data MongoDB Reference Documentation
Source Code
You can find the complete source code for this tutorial on Github.
Reactive: Spring Boot, MongoDB, Reactive CRUD example
Documentation: Spring Boot + Swagger 3 example (with OpenAPI 3)
Caching: Spring Boot Redis Cache example
Validation: Spring Boot Validate Request Body
Sort of off topic, but I’m having trouble integrating this with Spring Graphql. Are there any tweaks I would need to make from this tutorial?
Thanks for the clear cut explanation. This was the easiest, working and clear tutorial I could find for this topic.
No doubt everything is clear.
Thank you.
Is it just me or anyone else finds that mongo connection strings in application.properties are not put correctly. There is no connection string. I can understand if that was obvious but some reference to it for someone totally new would have been nice. Even the github doesn’t mention it.
In this example, do we need to manually start a database named bezkoder_db, or does Spring application starts it automatically when we edit application.properties file and run Spring app ?
Does “404 not found error” is related to that ?
Just wanna say that this tutorial is perfect, the written skill is real great!
I try to edit create function with @PostMapping as follows
MentorApplication mentorApplication = MentorApplicationRepository.save(new MentorApplication(application.getId(),application.getTopic(), application.getSubtopic(),application.getDiscussion(), application.getCreationDate(), application.getStatus()));
but in this line I get
“not make a static reference to the non-static method save(MentorApplication) from the type CrudRepository” error
my repository interface is as follows
public interface MentorApplicationRepository extends MongoRepository {
List findByIDContaining(String id);
List findByTopic(String topic);
List findByStatus(int status);
}
anybody got an error like this ? or knows how to solve it ?
Curious why do we need that CrossOrigin thing?
Hi, if you need only Rest API for testing, just remove it. But in practice, CORS is an important protocol for making cross-domain requests possible.
For example, the front-end JavaScript code served from
https://aaa.com
makes a request forhttps://bbb.com/data.json
.Wonder and clean explanation. Thanks for tutorial.
Very knowledge full Tutorial. Can you please implement this with UI along with TestCases and with more custom exception for understanding more.
Thank you soo muchh I’ve been trying to find an example that works and as simple as possible this worked perfectly with my understanding
I am getting Invalid CORS request in POSTMAN, Can you please help
Use the Postman standalone client instead of the Chrome extension.
Hi!
Thanks for all your help with this.
Im trying to run this sample but i have troubles whith Mongo.
Maybe it’s a newbie question but i’ve never used Mongo.
Cluster created with settings {hosts=[localhost:27017], mode=MULTIPLE, requiredClusterType=UNKNOWN, serverSelectionTimeout=’30000 ms’, maxWaitQueueSize=500}
Adding discovered server localhost:27017 to client view of cluster
Exception in monitor thread while connecting to server localhost:27017
com.mongodb.MongoSocketOpenException: Exception opening socket
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:70) ~[mongodb-driver-core-3.11.2.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:128) ~[mongodb-driver-core-3.11.2.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:117) ~[mongodb-driver-core-3.11.2.jar:na]
at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]
Caused by: java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:579) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:339) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:603) ~[na:na]
at com.mongodb.internal.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:64) ~[mongodb-driver-core-3.11.2.jar:na]
at com.mongodb.internal.connection.SocketStream.initializeSocket(SocketStream.java:79) ~[mongodb-driver-core-3.11.2.jar:na]
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:65) ~[mongodb-driver-core-3.11.2.jar:na]
… 3 common frames omitted
Spring boot finally starts but mongo never replies received requests.
Thanks for this man, everything worked perfectly 🙂
Do you have the github , and did you make it full stack?