Spring Boot con Spring Data MongoDB

35 minIntermedio

Una API de pedidos con Spring Boot 4 y un repositorio de Spring Data que guarda en TeselaDB con TLS.

Spring Data MongoDB se conecta a la base a través del driver oficial de Java, así que tus documentos y tus repositorios funcionan con TeselaDB sin cambios. Aquí montas una API de pedidos y la conectas al clúster con TLS.

Lo que vas a construir

Una aplicación de Spring Boot 4 con un documento Order, un repositorio con una consulta derivada y un controlador con POST /orders, GET /orders/{id} y GET /orders?customer=.

Requisitos

  • Java 21 o posterior.
  • Docker y mongosh, para arrancar TeselaDB con TLS y usuarios en tu máquina.

Prepara el entorno

Shell
docker run -d --name teseladb -p 27017:27017 viuzi/teseladb:latest \
  start --dbpath /data --bind_ip 0.0.0.0
until docker logs teseladb 2>&1 | grep -q '^ready'; do sleep 1; done
ROOT_PASSWORD=$(docker exec teseladb cat /data/root-credentials.txt | sed -n 's/^password=//p')
docker cp teseladb:/data/pki/ca.pem ./ca.pem
export TESELADB_URI="mongodb://root:$ROOT_PASSWORD@127.0.0.1:27017/?tls=true&tlsCAFile=./ca.pem&authSource=admin"
mongosh "$TESELADB_URI" --quiet --eval 'db.runCommand({ ping: 1 }).ok'
mongosh "$TESELADB_URI"

El contenedor arranca un nodo en modo seguro y su primer arranque crea la autoridad de certificación y el usuario root con una contraseña al azar. Las órdenes siguientes esperan a la línea ready, sacan esa contraseña y la CA y dejan en TESELADB_URI una cadena con TLS como root. Cuando mongosh responde 1, la última línea abre la sesión de root desde la que crearás el usuario de la aplicación.

1. Un usuario para la aplicación

JavaScript
db.getSiblingDB("admin").createUser({
  user: "shop-api",
  pwd: passwordPrompt(),
  roles: [{ role: "readWrite", db: "shop" }],
});

La aplicación no usa root, sino un usuario que solo lee y escribe en shop. Después, en una terminal nueva desde la misma carpeta donde quedó ca.pem, deja su cadena en TESELADB_URI sustituyendo <contraseña> por la que acabas de escribir. Lleva tls=true y authSource=admin, pero no tlsCAFile, porque la CA le llega a la aplicación por un paquete SSL de Spring Boot:

Shell
export TESELADB_URI="mongodb://shop-api:<contraseña>@127.0.0.1:27017/shop?tls=true&authSource=admin"

2. Crea el proyecto

Genera un proyecto Maven para Java 21 con los módulos web y Spring Data MongoDB desde start.spring.io, descomprímelo en la carpeta orders y copia dentro la CA, que la aplicación lee desde su carpeta de trabajo:

Shell
curl -s https://start.spring.io/starter.zip -d dependencies=web,data-mongodb -d type=maven-project \
  -d javaVersion=21 -d groupId=com.example -d artifactId=orders -o orders.zip
unzip orders.zip -d orders && cd orders
cp ../ca.pem .

3. Conéctalo a TeselaDB

En src/main/resources/application.properties. La CA del clúster va en un paquete SSL de Spring Boot, que el cliente de MongoDB usa como almacén de confianza. En Spring Boot 3, la propiedad de la cadena es spring.data.mongodb.uri:

spring.mongodb.uri=${TESELADB_URI}
spring.ssl.bundle.pem.teseladb.truststore.certificate=file:./ca.pem
spring.mongodb.ssl.bundle=teseladb

4. Escribe el documento, el repositorio y el controlador

En src/main/java/com/example/orders/. Spring Data genera la consulta de findByCustomer a partir de su nombre:

Java
package com.example.orders;

import java.time.Instant;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document("orders")
public record Order(@Id String id, String customer, double total, Instant createdAt) {}
Java
package com.example.orders;

import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface OrderRepository extends MongoRepository<Order, String> {
  List<Order> findByCustomer(String customer);
}
Java
package com.example.orders;

import java.time.Instant;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {
  private final OrderRepository orders;

  public OrderController(OrderRepository orders) {
    this.orders = orders;
  }

  @PostMapping
  @ResponseStatus(HttpStatus.CREATED)
  public Order create(@RequestBody Order order) {
    return orders.save(new Order(null, order.customer(), order.total(), Instant.now()));
  }

  @GetMapping("/{id}")
  public ResponseEntity<Order> read(@PathVariable String id) {
    return ResponseEntity.of(orders.findById(id));
  }

  @GetMapping
  public List<Order> byCustomer(@RequestParam String customer) {
    return orders.findByCustomer(customer);
  }
}

Probarlo

Arranca la aplicación con ./mvnw spring-boot:run. Después, en otra terminal, crea un pedido de Ana con un total de 30 y búscalo por su cliente con la ruta GET /orders?customer=, que lanza la consulta derivada de findByCustomer del repositorio:

Shell
./mvnw spring-boot:run
curl -s -X POST localhost:8080/orders -H "content-type: application/json" -d '{"customer":"Ana","total":30}'
curl -s "localhost:8080/orders?customer=Ana"

La segunda orden devuelve una lista con el pedido de Ana, con su id y su fecha de creación.

Limpiar

JavaScript
db.getSiblingDB("shop").orders.deleteMany({ customer: "Ana" });

Para la aplicación con Ctrl+C y ejecuta esta orden en mongosh para borrar los pedidos de prueba, todos con Ana como cliente. Al final borra también el contenedor con la orden de abajo: el nodo guarda los datos dentro, sin volumen, así que con él desaparecen el usuario y los pedidos y queda libre el puerto 27017.

Shell
docker rm -f teseladb

Siguientes pasos

  • Java, con el driver de Java sin Spring.
  • ODM y ORM, con Mongoose y Beanie.