Spring Rest Repositories para acelerar MVP e PoC
Tags: programming, java, mvp, webdev
Published: 2022-08-09 23:25:00

## 1. O Problema que queremos resolver
As vezes precisamos criar um MVP e testar uma ideia rápido para ter feedback o quanto antes e é aqui que o Spring REST Repositories brilha acelerando muito o desenvolvimento de uma API REST.
Neste artigo vamos criar um CRUD de uma API REST utilizando alguns recursos muito interessantes no ecosistema Spring.

**_O que vamos usar:_**

- Spring Data/JPA para fazer o trabalho duro de ORM e manipular os dados no Banco.
- Spring REST Repositories para expor nosso CRUD através de uma API REST.
- H2 como Banco de Dados Relacional em memória.
- Lombok para automatizar nossos Getters, Setters, Constructors, ToString.

## 2. A Solução
Vamos usar o site `SPRING INITIALIZR` para criação do projeto, acesse a URL: https://start.spring.io/ e configure conforme a imagem e ao final clique em “**Generate Project**”

![SPRING INITIALIZR](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t81hjqg2k2kpucxbwq62.png)

> Na sessão de Dependences digite: `Web`, `JPA`, `H2`, `Rest Repositories`. Após clicar em “Generate Project” será feito o download do projeto em um arquivo .zip, descompacte e importe em sua IDE de preferencia.

Agora vamos criar nosso modelo “Person”, que neste caso será bem simple pois o Foco é o “Spring Rest Repositories”.

```java
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Data
@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String firstName;
    private String lastName;

}
```

Aqui usamos algumas annotations:
- `Entity`: do conhecido JPA para tornar nosso modelo um ORM.
- `Id`: do JPA para setar nosso id como Chave primária.
- `GeneratedValue`: do JPA para setar nosso id como AutoIncrement.
- `Data`: do Lombok para criar nossos Getters and Setters.

Com nosso modelo **ORM** criado podemos criar nossa Interface Repository, e aqui está o pulo do gato onde acontece toda a Magia do **Spring**.
 

```java
import java.util.List;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {

   List<Person> findByLastName(@Param("name") String name);

}
```

> Aqui cabe alguns comentários:
> Quem já está familiarizado com Spring Data sabe que é comum anotar essa Interface com `@Repository` e aqui estamos anotando com `@RepositoryRestResource`.
> Com essa annotation o Spring irá fazer uma linda magia e apenas com essa interface ele já ira criar nossa camada de Repository que saberá receber nosso modelo Person e executar todas as tarefas de **CRUD** em nosso Banco de Dados, além disso ele criar uma camada de Controller internamente e irá expor esse nosso CRUD através de uma **API REST**.
> Isso mesmo, não teremos que criar nem a nossa camada Service nem a nossa camada Controller, Automágicamente o Spring fará esse trabalha para nós através do **Spring Rest Repositories** =).
> E ainda herdamos nossa interface de “PagingAndSortingRepository” que irá gerar nossas listas paginada, mais uma vez Automágicamente =).

Neste momento se executarmos nossa aplicação já temos uma **API REST** com todas as operações **CRUD** de Person além de um endpoint de consulta por “**LastName**”.

Aqui a lista de endpoints disponíveis na nossa **API REST**:

```
GET http://localhost:8080
GET http://localhost:8080/people
GET http://localhost:8080/people/{id}
GET http://localhost:8080/people/search
POST http://localhost:8080/people
PUT http://localhost:8080/people/{id}
PATCH http://localhost:8080/people/{id}
DELETE http://localhost:8080/people/{id}
```

> **PUT** substitui um registro inteiro. Campos não fornecidos serão substituídos por null. 
**PATCH** pode ser usado para atualizar um subconjunto de itens.

Segue alguns exemplos de Teste utilizando o **curl**.

**POST**:
```
$ curl -i -X POST -H "Content-Type:application/json" -d '{"firstName": "Frodo", "lastName": "Baggins"}' 


http://localhost:8080/people
HTTP/1.1 201 
Location: http://localhost:8080/people/1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Feb 2019 21:03:47 GMT
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/1"
    },
    "person" : {
      "href" : "http://localhost:8080/people/1"
    }
  }
}%
```

**GET All**:

```
$ curl http://localhost:8080/people  

     
{
  "_embedded" : {
    "people" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/1"
        },
        "person" : {
          "href" : "http://localhost:8080/people/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8080/profile/people"
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}%

```

> Perceba neste exemplo que o resultado é paginado com: size, totalElements, totalPages, number.

## Adicionando Swagger

1. Adicione as dependências no `pom.xml`

```xml
<!-- Swagger -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-data-rest</artifactId>
   <version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>3.0.0-SNAPSHOT</version>
</dependency>
```

2. Adicione também no `pom.xml` o repositório da dependência:

```xml
<repositories>
   <repository>
      <id>jcenter-snapshots</id>
      <name>jcenter</name>
      <url>http://oss.jfrog.org/artifactory/oss-snapshot-local/</url>
   </repository>
</repositories>
```

> É importante adicionar esse repository porque no repositório central ainda não tem essa versão do swagger, e é nela que estão implementando essa feature do Swagger para suportar o Spring Rest Repositories.

3. A classe `Configuration` do **Swagger**

```java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.data.rest.configuration.SpringDataRestConfiguration;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

@Configuration
@EnableSwagger2WebMvc
@Import(SpringDataRestConfiguration.class)
public class SwaggerConfiguration {

    private static final String PATH_MAPPING = "/";

    private static final String PACKAGE_BASE = "com.eprogramar.demorepositoryrestresource";

    @Value("${info.app.version}")
    private String projectVersion;

    @Value("${info.app.name}")
    private String projectName;

    @Value("${info.app.description}")
    private String projectDescription;

    @Value("${spring.profiles.active}")
    private String activeProfile;

    @Bean
    Docket rsApi() {
        return new Docket(DocumentationType.SWAGGER_12)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .pathMapping(PATH_MAPPING)
                .useDefaultResponseMessages(false);
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(projectName)
                .description(projectDescription + " - Profile: " + activeProfile)
                .version(projectVersion)
                .build();
    }

}
```

Adicionei algumas properties no `application.properites` só pra facilitar as coisas não seria uma coisa obrigatória.

Ok, executando a aplicação agora podemos ver todos os endpoints documentados, acesse: `http://localhost:8080/swagger-ui.html`
![Swagger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/danovmjhy8p152keuefj.png)

![Disclimer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7e9p5f5al1kuizz5awh.png)

Veja Também meu video:
“**Como Criar uma API Rest Kotlin com Persistencia e Paginação em 6 minutos**”

{% youtube iaOisrsTy48 %}

## 3. Conclusão
Neste artigo aprendemos utilizar algumas magias do Spring onde ele automatizou a criação de nosso **ORM** e ainda criou nossa **API REST** automágicamente.
Com isso ganhamos muito tempo quando precisamos criar **CRUD** e expor através de uma **API REST**.
Claro, em caso simples onde não precisamos de regras para nosso **CRUD** isso pode ser uma boa opção, se combinar com Bean Validation para validar alguns campos e ainda utilizar de um Handle para interceptar os Erros e soltar uma mensagem mais amigável fica uma solução bem legal.
Em casos onde tem muitas regras ai é aconselhável utilizar o modelo mais conceitual com a Camada **Service** e **Controller**.

O código pode ser visto no Github:
https://github.com/e-programar/demo-repository-rest-resource

Referencia: https://spring.io/guides/gs/accessing-data-rest/