Crear API REST con Spring

En este artículo, crearemos una API REST con el framework Spring, el framework más utilizado para la plataforma Java. Para ello, utilizaremos Spring boot que nos facilitará su configuración (uso de dependencias, conexión con la base de datos, despliegue de servidor, etc).

Debemos tener instalado Java, Maven en nuestro PC y como entorno de desarrollo utilizaremos Eclipse.

Creando proyecto con SpringBoot

Como hemos comentado, utilizaremos Springboot, que es una de las tecnologías dentro del mundo de spring, que nos facilita el desarrollo de aplicaciones con este framework, ya que nos facilita el trabajo con de las dependencias (Maven/Grandle), del despliegue del servidor, ya que nos provee de uno (a parte de mucha otra configuración).

Para comenzar, nos dirigimos a la página https://start.spring.io/ dónde descargaremos nuestro proyecto:

  1. Project: En la primera opción, elegimos si queremos trabajar con maven o gradle, en este ejemplo, utilizaremos Maven.
  2. Language: En la segunda opción, elegimos con que lenguaje trabajar, Java.
  3. Springboot: En esta opción, elegimos la versión que queremos utilizar, en este caso 2.1.5
  4. Project Metadata: El nombre de nuestro proyecto.
  5. Dependencies: En el buscador de dependecias, buscaremos y añadiremos WEB para crear nuestra API REST.

Daremos click sobre el botón Generate the project, comenzará la descarga y una vez descargado lo descomprimiremos.

Incluyendo proyecto Spring boot en Eclipse

Ahora importaremos nuestro proyecto spring boot en Eclipse, para ello, iremos a la barra de eclipse e iremos a file/import/Maven/Existing Maven Porjects y daremos a browse, dónde buscaremos el proyecto que acabamos de descargar, nos fijamos en que este el check que nos aparecerá del pom.xml marcado y daremos a next.

Nuestro proyecto ya aparecerá en eclipse y comenzará a descargar las dependencias de su archivo pom.xml.

Configurando spring boot

Dependencias

Para aprender a añadir nuevas dependencias en nuestro proyecto, vamos a dirigirnos a pom.xml, que se encuentra en la raiz de nuestro proyecto, ahí nos aparecerán las dependencias que se han agregado al añadirle en spring init la dependencia WEB. En este archivo, en el apartado de dependencias, vamos a añadir debajo de las que ya tenemos, tres más, una será para trabajar con JPA, otra para conectarnos a nuestra base mysql y otra para que refresque el servidor cada vez que haya algún cambio:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jlguisado.webjob</groupId>
	<artifactId>api</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>api</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Una vez hecho esto, actualizaremos nuestro proyecto con estas nuevas dependencias, haciendo click derecho sobre su carpeta, maven/update project.

Base de datos

En este ejemplo, estoy utilizando xampp para trabajar con la base de datos que he llamado api y una tabla llamada user que tiene la siguiente estructura:

Una vez creada nuestra base de datos y su tabla user, vamos a volver a nuestro proyecto spring y nos dirigimos al archivo application.properties, que se encuentra en src/main/resources e indicaremos la ruta de la base de datos, su usuario y contraseña:

#
# JDBC Properties
#
spring.datasource.url = jdbc:mysql://localhost:3306/api?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC 
#direccion de la base de datos, puede variar el puerto y nombre de la bd

spring.datasource.username=root #usuario que utilizas en la bd
spring.datasource.password=root #contraseña que utilizas en la bd

Una vez hecho esto, ya estaríamos listo para crear nuestra API REST.

Creando API REST con Spring boot

Para comenzar, os mostraré el carpetado que va a tener nuestro API REST, tendréis que crear estos paquetes con estas clases:

ApiApplication.java

Es la clase que arranca nuestra aplicación springboot, dando click derecho sobre la clase y run as application:

package com.nigmacode.apirest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

}

Paquete entity

En este paquete tenemos nuestro clase User.java y utilizaremos las @anotaciones JPA para relacionarla con nuestra tabla user, quedaría de la siguiente forma:

package com.nigmacode.apirest.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

@Entity
@Table(name="user")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    private int id;

    @Column(name="email")
    private String email;

    @Column(name="password")
    private String password;

    @Column(name="created_at")
    @CreationTimestamp
    private Date createdAt;

    @Column(name="updated_at")
    @UpdateTimestamp
    private Date updatedAt;

    public User() {}

    public User(int id, String email, String password) {
        this.id = id;
        this.email = email;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", email=" + email + ", password=" + password + ", createdAt=" + createdAt
                + ", updatedAt=" + updatedAt + "]";
    }


}

Paquete DAO

En UserDAO, crearemos los métodos que utilizará la clase UserDAOimpl, para conectarse con nuestra base de datos.

Interfaz userDAO:

package com.nigmacode.apirest.dao;

import java.util.List;

import com.nigmacode.apirest.entity.User;


public interface UserDAO {

    public List<User> findAll();

    public User findById(int id);

    public void save(User user);

    public void deleteById(int id);
}

En nuestra clase UserDAOImpl, implementamos la interfaz, le añadiremos la anotación @Repository que indica que es un DAO,y mediante la anotación @Autowired inyectaremos EntityManager, que lo utilizaremos para crear una sessión y poder enviar las peticiones a la base de datos en cada método:

package com.nigmacode.apirest.dao;

import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.nigmacode.apirest.entity.User;

@Repository
public class UserDAOImpl implements UserDAO{

    @Autowired
    private EntityManager entityManager;

    @Override
    public List<User> findAll() {
        Session currentSession = entityManager.unwrap(Session.class);

        Query<User> theQuery = currentSession.createQuery("from User", User.class);

        List<User> users = theQuery.getResultList();

        return users;

    }

    @Override
    public User findById(int id) {
        Session currentSession = entityManager.unwrap(Session.class);

        User user = currentSession.get(User.class, id);

        return user;
    }

    @Override
    public void save(User user) {
        Session currentSession = entityManager.unwrap(Session.class);

        currentSession.saveOrUpdate(user);  

    }

    @Override
    public void deleteById(int id) {
        Session currentSession = entityManager.unwrap(Session.class);

        Query<User> theQuery = currentSession.createQuery("delete from User where id=:idUser");

        theQuery.setParameter("idUser", id);
        theQuery.executeUpdate();

    }


}

Paquete Service

Como en el paquete anterior tendremos una clase y una interface. El servicio será el que hará de interemediario entre el DAO y el controlador(La clase que gestionará las peticiones de la API que veremos más adelante).

La interfaz de service tendría esta estructura UserService.java:

package com.nigmacode.apirest.service;

import java.util.List;

import com.nigmacode.apirest.entity.User;

public interface UserService {

    public List<User> findAll();

    public User findById(int id);

    public void save(User user);

    public void deleteById(int id);
}

Y la clase UserServiceImpl.java, será implementada por la interfaz anterior. Le añadiremos la anotación @Service, para indicar que es un servicio y también de @Autowired para inyectar nuestro DAO y hacer uso de él:

package com.nigmacode.apirest.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.nigmacode.apirest.dao.UserDAO;
import com.nigmacode.apirest.entity.User;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public List<User> findAll() {
        List<User> listUsers= userDAO.findAll();
        return listUsers;
    }

    @Override
    public User findById(int id) {
        User user = userDAO.findById(id);
        return user;
    }

    @Override
    public void save(User user) {
        userDAO.save(user);

    }

    @Override
    public void deleteById(int id) {
        userDAO.deleteById(id);
    }

}

Paquete controller

Por último, en el paquete controller tendremos la clase UserRestController.java que será la encargada de gestionar las peticiones que se hagan a nuestra API, esta clase en más extensa y será explicada mediante comentarios en el siguiente código:

package com.nigmacode.apirest.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.nigmacode.apirest.entity.User;
import com.nigmacode.apirest.service.UserService;

//Indiciamos que es un controlador rest
@RestController
@RequestMapping("/api") //esta sera la raiz de la url, es decir http://127.0.0.1:8080/api/

public class UserRestController {

    //Inyectamos el servicio para poder hacer uso de el
    @Autowired
    private UserService userService;

    /*Este método se hará cuando por una petición GET (como indica la anotación) se llame a la url 
    http://127.0.0.1:8080/api/users*/
    @GetMapping("/users")
    public List<User> findAll(){
        //retornará todos los usuarios
        return userService.findAll();
    }

    /*Este método se hará cuando por una petición GET (como indica la anotación) se llame a la url + el id de un usuario
    http://127.0.0.1:8080/api/users/1*/
    @GetMapping("/users/{userId}")
    public User getUser(@PathVariable int userId){
        User user = userService.findById(userId);

        if(user == null) {
            throw new RuntimeException("User id not found -"+userId);
        }
        //retornará al usuario con id pasado en la url
        return user;
    }

    /*Este método se hará cuando por una petición POST (como indica la anotación) se llame a la url
    http://127.0.0.1:8080/api/users/  */
    @PostMapping("/users")
    public User addUser(@RequestBody User user) {
        user.setId(0);

        //Este metodo guardará al usuario enviado
        userService.save(user);

        return user;

    }
    /*Este método se hará cuando por una petición PUT (como indica la anotación) se llame a la url
    http://127.0.0.1:8080/api/users/  */
    @PutMapping("/users")
    public User updateUser(@RequestBody User user) {

        userService.save(user);

        //este metodo actualizará al usuario enviado

        return user;
    }

    /*Este método se hará cuando por una petición DELETE (como indica la anotación) se llame a la url + id del usuario
    http://127.0.0.1:8080/api/users/1  */
    @DeleteMapping("users/{userId}")
    public String deteteUser(@PathVariable int userId) {

        User user = userService.findById(userId);

        if(user == null) {
            throw new RuntimeException("User id not found -"+userId);
        }

        userService.deleteById(userId);

        //Esto método, recibira el id de un usuario por URL y se borrará de la bd.
        return "Deleted user id - "+userId;
    }

}

Solucionar problema CORS en nuestra API

Para que nuestra aplicación no nos de problemas a la hora de utilizarla desde otra dirección, debemos configurarla para que no de error de CORS, para ello debemos seguir las intrucciones de nuestro artículo 👉https://www.nigmacode.com/java/Solucionar-CORS-en-Spring👈


De está forma, ya tendríamos nuestra API REST con Spring boot lista, ya solo quedaría levantar el servidor, como hemos dicho anteriormente, dando click derecho sobre ApiApplication.java y run as application