Ir al contenido

Implementación de un API REST con Spring

Spring MVC es la tecnología que usamos para construir servicios web que siguen la arquitectura REST. Una clase anotada con @RestController agrupa las implementaciones correspondientes a cada verbo HTTP (GET, POST, PUT, DELETE) asociado a cada URI de un recurso. Por ejemplo, una clase controlador puede reunir todos los servicios que se ofrecen para el recurso Book.

Cuando un cliente HTTP invoca un servicio, por ejemplo POST /books, ocurre lo siguiente:

  1. La petición POST /books es procesada del lado del servidor por Spring.
  2. Spring identifica el endpoint: el método asociado con la ruta /books y con el verbo POST.
  3. El método se ejecuta.
  4. Al terminar, la respuesta se transforma al formato del protocolo HTTP y se envía de regreso al cliente en la representación elegida por el desarrollador —normalmente JSON.

En la petición viaja un objeto JSON; cuando Spring la procesa, el método correspondiente recibe un objeto Java (un DTO, por ejemplo BookDTO). Recorre el siguiente ejemplo para ver el camino completo de una petición a través de las tres capas:

Recorre una petición completa

Cambia de operación y sigue el dato desde el cliente hasta la base de datos y de regreso.

EscenarioGET /api/artistas/1
ClientePaso 1 de 10
Enviar GET

El cliente solicita el artista con identificador 1.

GET /api/artistas/1

Implementación de las representaciones de los recursos

Sección titulada «Implementación de las representaciones de los recursos»

Mientras que el cliente de los servicios REST utiliza representaciones JSON de los recursos, la implementación de esos recursos usa objetos de una clase Java. Las instancias de esa clase solo tienen los valores de los atributos y se suelen llamar POJO (Plain Old Java Object). Por convención, cada clase Java que representa un recurso se llama RecursoDTO, donde Recurso es el nombre del recurso: para el recurso Book, la clase es BookDTO.

Cómo se convierte el JSON en un DTO (y viceversa)

Sección titulada «Cómo se convierte el JSON en un DTO (y viceversa)»

Nadie escribe a mano el código que lee cada propiedad del JSON y la asigna al atributo correspondiente del DTO. Esa conversión la hace un mapper: una librería que, dados un JSON y una clase Java, empareja cada propiedad con el atributo del mismo nombre. En Java, la librería estándar para esto es Jackson, y su clase principal es ObjectMapper.

Explora el ejemplo: un EstudianteDTO con su carnet, su nombre y la lista de cursos que aprobó, donde cada curso es a su vez otro objeto con su propio nombre.

JSON

{
  "carnet": 12345,
  "nombre": "Camila Ruiz",
  "cursosAprobados": [
    { "nombre": "Bases de Datos" },
    { "nombre": "Ingeniería de Software" }
  ]
}

Java (DTOs)

@Data @NoArgsConstructor @AllArgsConstructor
public class EstudianteDTO {
    private Long carnet;
    private String nombre;
    private List<CursoDTO> cursosAprobados;
}

public class CursoDTO {
    private String nombre;
}

Haz clic en una propiedad del JSON o en un atributo de la clase Java para ver con cuál se corresponde.

✅ Con estos nombres, el mapper asigna 12345 a EstudianteDTO.carnet.

Supongamos que queremos implementar los servicios REST de la página de diseño para una entidad City: crear una ciudad, obtener todas las ciudades, obtener una por id, actualizarla y borrarla.

Método Path Acción Parámetros Cuerpo Retorno
GET /cities Lista los registros de City Colección de City
GET /cities/:id Obtiene una instancia de City con identificador id @PathVariable id Atributos de la instancia
POST /cities Crea una instancia nueva de City Atributos de la instancia a crear Instancia creada, con su id
PUT /cities/:id Actualiza la instancia con identificador id @PathVariable id Objeto JSON de City Instancia actualizada
DELETE /cities/:id Borra la instancia con identificador id @PathVariable id

La clase CityController tendrá entonces los siguientes métodos:

Método Descripción
List<CityDTO> findAll() Retorna la lista de ciudades.
CityDTO create(CityDTO city) Crea una ciudad con la información recibida por parámetro.
CityDTO findOne(Long id) Retorna la ciudad identificada con id.
CityDTO updateCity(Long id, CityDTO city) Actualiza la información de la ciudad identificada con id.
void delete(Long id) Borra la ciudad identificada con id.

Para que Spring procese una clase como recurso, esta debe estar anotada: @RestController indica que la clase es un controlador, y @RequestMapping indica el path del recurso.

Anotación Método Descripción
@GetMapping List<CityDTO> findAll() Retorna la lista de ciudades.
@PostMapping CityDTO create(CityDTO city) Crea una ciudad con la información recibida por parámetro.
@GetMapping CityDTO findOne(Long id) Retorna la ciudad identificada con id.
@PutMapping CityDTO update(Long id, CityDTO city) Actualiza la ciudad id con la información de city.
@DeleteMapping void delete(Long id) Borra la ciudad identificada con id.

Cuando definimos el servicio GET /cities, cities es el path. Cuando todos los métodos de una clase recurso comparten el mismo path inicial, ese path se anota al comienzo de la clase:

@RequestMapping("/cities")
public class CityController {
// ...
}

Cuando el path necesita más información que la del comienzo de la clase —por ejemplo GET /cities/id, donde id es el identificador numérico de la ciudad— se anota directamente sobre el método:

@GetMapping(value = "/{id}")
public CityDTO findOne(@PathVariable("id") Long id) throws EntityNotFoundException {
// ...
}

@PathVariable("id") conecta el segmento {id} de la ruta con el parámetro id del método: Spring lo extrae del path y lo convierte al tipo declarado, en este caso Long.

1. ¿Qué anotación identifica una clase como controlador REST en Spring?
2. Para el servicio DELETE /cities/id, ¿qué anotación se usa sobre el método del controller?
3. En @GetMapping(value="/{id}") public CityDTO findOne(@PathVariable("id") Long id), ¿de dónde toma Spring el valor de id?
4. ¿Qué recibe el método de un @RestController cuando el cliente envía un JSON en el body de un POST?
5. Si todos los métodos de un controller comparten el path inicial /cities, ¿dónde conviene anotarlo?