Skip to content
Spring Boot sb mongodb 3 min read

MongoRepository CRUD

MongoRepository<T, ID> is the declarative way to persist documents. You extend the interface, Spring generates the implementation at runtime, and you immediately get CRUD plus pagination, sorting, and derived query methods — the same model as JpaRepository.

The repository interface

Extend MongoRepository<T, ID> where T is your @Document type and ID is the @Id type (usually String).

public interface ProductRepository extends MongoRepository<Product, String> {
}

That alone gives you the inherited CRUD surface:

MethodPurpose
save(T) / saveAll(...)Insert or replace a document
findById(ID)Optional<T> lookup by _id
findAll() / findAll(Sort)Read all, optionally sorted
findAll(Pageable)Page through results
existsById(ID)Cheap existence check
count()Document count
deleteById(ID) / delete(T)Remove a document

The document

@Document("products")
public record Product(@Id String id, String name, String category, BigDecimal price) { }

Derived query methods

Spring parses method names and builds the MongoDB query for you — findBy + property + optional keyword.

public interface ProductRepository extends MongoRepository<Product, String> {

    List<Product> findByCategory(String category);

    List<Product> findByPriceLessThan(BigDecimal max);

    List<Product> findByNameContainingIgnoreCase(String fragment);

    Optional<Product> findFirstByCategoryOrderByPriceAsc(String category);

    long countByCategory(String category);

    void deleteByCategory(String category);
}
MethodMongoDB filter
findByCategory{ "category": ?0 }
findByPriceLessThan{ "price": { "$lt": ?0 } }
findByPriceBetween{ "price": { "$gte": ?0, "$lte": ?1 } }
findByCategoryAndPrice{ "category": ?0, "price": ?1 }

For anything more complex, write a JSON @Query or use MongoTemplate. See Queries & @Query.

A service layer

Keep business logic in a @Service and inject the repository via the constructor.

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository repository;

    public Product create(Product product) {
        return repository.save(product);
    }

    public Product getById(String id) {
        return repository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException(id));
    }

    public List<Product> byCategory(String category) {
        return repository.findByCategory(category);
    }

    public Product update(String id, Product changes) {
        Product existing = getById(id);
        Product merged = new Product(existing.id(), changes.name(),
                changes.category(), changes.price());
        return repository.save(merged);
    }

    public void delete(String id) {
        repository.deleteById(id);
    }
}

Note: save performs an upsert by _id: if the document’s id is null it inserts and returns the generated id; if the id exists it replaces the whole document. To update only specific fields, use MongoTemplate’s partial updates — see MongoTemplate.

A REST controller

Expose the service over HTTP with a @RestController.

@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService service;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product create(@RequestBody Product product) {
        return service.create(product);
    }

    @GetMapping("/{id}")
    public Product getOne(@PathVariable String id) {
        return service.getById(id);
    }

    @GetMapping
    public List<Product> byCategory(@RequestParam String category) {
        return service.byCategory(category);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable String id) {
        service.delete(id);
    }
}

The exchange

Creating a product:

POST /api/products
Content-Type: application/json

{ "name": "Keyboard", "category": "peripherals", "price": 79.99 }

Output:

{
  "id": "65f1c2a8e4b0a1d2c3e4f567",
  "name": "Keyboard",
  "category": "peripherals",
  "price": 79.99
}

The generated id is MongoDB’s _id (an ObjectId serialized as a string). Querying by category returns an array:

GET /api/products?category=peripherals
[
  { "id": "65f1c2a8e4b0a1d2c3e4f567", "name": "Keyboard", "category": "peripherals", "price": 79.99 },
  { "id": "65f1c2a8e4b0a1d2c3e4f568", "name": "Mouse", "category": "peripherals", "price": 39.50 }
]

Tip: Map documents to DTOs before returning them when the document holds fields you do not want to expose. See Entity vs DTO.

Last updated June 13, 2026
Was this helpful?