Building a Spring Boot CRUD API with MongoDB
A Spring Boot CRUD API backed by MongoDB is a practical foundation for customer portals, inventory systems and mobile applications. Spring Data MongoDB removes much of the database boilerplate while keeping the application structure familiar to Java developers who have worked with repositories and service classes.
This example builds a small product API with create, read, update and delete operations. The design suits an Australian application serving users in Sydney, Melbourne or Brisbane, while leaving room for validation, authentication, pagination and cloud deployment as the system grows.
Create The Spring Boot Project
Generate a Spring Boot project with Java 17 or later and add Spring Web, Spring Data MongoDB and Validation. Maven users can include these dependencies in pom.xml:
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
For local development, MongoDB can run through Docker or a native installation. Store the connection string in application.properties rather than embedding credentials in Java code:
spring.data.mongodb.uri=mongodb://localhost:27017/catalogue
server.port=8080
For a hosted application, MongoDB Atlas provides Australian regions, including Sydney. Selecting a nearby region can reduce latency for customers who commonly use the service during busy morning and evening periods.
Model Documents And Repositories
MongoDB stores records as BSON documents, so the product model does not need a relational table definition. The @Document annotation identifies the collection, while @Id maps the generated MongoDB identifier.
@Document("products")
public class Product {
@Id
private String id;
@NotBlank
private String name;
@PositiveOrZero
private BigDecimal price;
private String category;
// constructors, getters and setters
}
BigDecimal is preferable to double for prices, particularly when an Australian store displays amounts in Australian dollars. A real commerce system should also define how GST is represented and calculated rather than relying on floating-point arithmetic.
The repository interface gives the application standard persistence operations without manually writing MongoDB queries:
public interface ProductRepository
extends MongoRepository<Product, String> {
List<Product> findByCategoryIgnoreCase(String category);
}
Spring Data derives the category query from the method name. Custom filters can later use @Query, MongoDB criteria or pagination through Pageable.
Add The Service And CRUD Endpoints
A service layer keeps business rules outside the HTTP controller. It can also translate a missing record into a clear exception instead of returning null throughout the application.
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public List<Product> findAll() {
return repository.findAll();
}
public Product findById(String id) {
return repository.findById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND, "Product not found"));
}
public Product save(Product product) {
return repository.save(product);
}
public void delete(String id) {
repository.delete(findById(id));
}
}
The controller exposes conventional REST endpoints. POST /api/products creates a document, GET reads it, PUT replaces it and DELETE removes it.
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping
public List<Product> all() {
return service.findAll();
}
@GetMapping("/{id}")
public Product one(@PathVariable String id) {
return service.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product create(@Valid @RequestBody Product product) {
return service.save(product);
}
@PutMapping("/{id}")
public Product update(@PathVariable String id,
@Valid @RequestBody Product product) {
product.setId(id);
return service.save(product);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable String id) {
service.delete(id);
}
}
Validate Requests And Handle Errors
Bean Validation rejects blank names and negative prices when the controller receives a request. Add a global exception handler so clients receive predictable JSON rather than a default HTML error page.
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<Map<String, String>> validation(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors()
.forEach(error -> errors.put(
error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
}
For a public service, add authentication and authorisation before allowing writes. Spring Security with JWT is a common choice when a separate web or mobile client calls the API. Rate limiting, audit logging and CORS rules should also be configured deliberately rather than opened globally.
Australian applications may handle names, addresses and order details covered by the Privacy Act 1988 and the Australian Privacy Principles. Collect only the fields required for the business purpose, protect credentials, and assess whether records should remain in an Australian data region.
Test And Prepare The API For Production
Use MockMvc or @WebMvcTest for controller behaviour and @DataMongoTest for repository queries. Test the full lifecycle: create a product, retrieve it by ID, update its price, delete it and verify that a later lookup returns HTTP 404. Test invalid input as well, including an empty name and a negative amount.
Before release, add indexes for fields used frequently in searches and use pagination rather than returning thousands of documents in one response. Actuator health checks, structured logs and environment variables make the service easier to operate on platforms used by Australian teams across Melbourne, Perth and regional areas.
A polished API can become the backend for a responsive shop, booking platform or mobile product. Teams delivering Java web development projects can extend this foundation with OpenAPI documentation, role-based access, payment integration and deployment pipelines while keeping the MongoDB repository layer isolated from presentation concerns.