Se você está começando sua jornada no desenvolvimento Java e quer aprender a criar APIs REST modernas, o Spring Boot é a ferramenta ideal para começar. Neste artigo, vamos construir uma API REST completa do zero, explicando cada conceito de forma simples e prática.
O que é Spring Boot?
Spring Boot é um framework Java que simplifica drasticamente a criação de aplicações Spring. Ele elimina a necessidade de configurações complexas, oferecendo:
- Configuração automática: Detecta automaticamente as dependências e configura o projeto
- Servidor embutido: Não precisa instalar Tomcat ou outros servidores
- Gerenciamento de dependências: Resolve conflitos de versões automaticamente
- Produção pronta: Inclui recursos como métricas, verificação de saúde e monitoramento
Por que Usar Spring Boot para APIs REST?
Criar APIs REST com Spring Boot oferece várias vantagens:
- Rapidez no desenvolvimento: Menos código boilerplate
- Padrões estabelecidos: Segue as melhores práticas da indústria
- Ecossistema rico: Integração fácil com bancos de dados, segurança, testes
- Comunidade ativa: Vasta documentação e suporte
Configurando o Ambiente
Pré-requisitos
Antes de começar, certifique-se de ter:
- Java 11 ou superior instalado
- IDE (IntelliJ IDEA, Eclipse ou VS Code)
- Maven ou Gradle (usaremos Maven neste tutorial)
Criando o Projeto
A forma mais fácil de criar um projeto Spring Boot é através do Spring Initializr:
- Acesse https://start.spring.io/
- Selecione:
- Project: Maven Project
- Language: Java
- Spring Boot: 3.1.x (versão estável mais recente)
- Packaging: Jar
- Java: 11 ou superior
- Adicione as dependências:
- Spring Web
- Spring Data JPA
- H2 Database (para desenvolvimento)
- Spring Boot DevTools
- Click em “Generate” para baixar o projeto
Estrutura do Projeto
Após extrair o projeto, você verá esta estrutura:
meu-projeto/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com.exemplo.projeto/
│ │ │ └── ProjetoApplication.java
│ │ └── resources/
│ │ ├── application.properties
│ │ └── static/
│ └── test/
├── pom.xml
└── mvnw
Construindo Nossa Primeira API
Vamos criar uma API simples para gerenciar produtos. Começaremos definindo nossa entidade.
1. Criando a Entidade Produto
Crie a classe Produto.java
no pacote principal:
package com.exemplo.projeto.model;
import jakarta.persistence.*;
@Entity
@Table(name = "produtos")
public class Produto {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nome;
@Column(nullable = false)
private Double preco;
private String descricao;
// Construtores
public Produto() {}
public Produto(String nome, Double preco, String descricao) {
this.nome = nome;
this.preco = preco;
this.descricao = descricao;
}
// Getters e Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getNome() { return nome; }
public void setNome(String nome) { this.nome = nome; }
public Double getPreco() { return preco; }
public void setPreco(Double preco) { this.preco = preco; }
public String getDescricao() { return descricao; }
public void setDescricao(String descricao) { this.descricao = descricao; }
}
Explicando as anotações:
@Entity
: Marca a classe como uma entidade JPA@Table
: Define o nome da tabela no banco@Id
: Marca o campo como chave primária@GeneratedValue
: Configura a geração automática do ID@Column
: Configura propriedades da coluna
2. Criando o Repository
O Repository é responsável pelo acesso aos dados:
package com.exemplo.projeto.repository;
import com.exemplo.projeto.model.Produto;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProdutoRepository extends JpaRepository<Produto, Long> {
// Métodos personalizados (se necessário)
List<Produto> findByNomeContaining(String nome);
List<Produto> findByPrecoLessThan(Double preco);
}
O JpaRepository
já fornece métodos prontos como:
save()
– salvar/atualizarfindById()
– buscar por IDfindAll()
– listar todosdeleteById()
– deletar por ID
3. Criando o Controller
O Controller gerencia as requisições HTTP:
package com.exemplo.projeto.controller;
import com.exemplo.projeto.model.Produto;
import com.exemplo.projeto.repository.ProdutoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/produtos")
public class ProdutoController {
@Autowired
private ProdutoRepository produtoRepository;
// GET /api/produtos - Listar todos os produtos
@GetMapping
public List<Produto> listarTodos() {
return produtoRepository.findAll();
}
// GET /api/produtos/{id} - Buscar produto por ID
@GetMapping("/{id}")
public ResponseEntity<Produto> buscarPorId(@PathVariable Long id) {
Optional<Produto> produto = produtoRepository.findById(id);
return produto.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/produtos - Criar novo produto
@PostMapping
public Produto criar(@RequestBody Produto produto) {
return produtoRepository.save(produto);
}
// PUT /api/produtos/{id} - Atualizar produto
@PutMapping("/{id}")
public ResponseEntity<Produto> atualizar(@PathVariable Long id,
@RequestBody Produto produtoAtualizado) {
return produtoRepository.findById(id)
.map(produto -> {
produto.setNome(produtoAtualizado.getNome());
produto.setPreco(produtoAtualizado.getPreco());
produto.setDescricao(produtoAtualizado.getDescricao());
return ResponseEntity.ok(produtoRepository.save(produto));
})
.orElse(ResponseEntity.notFound().build());
}
// DELETE /api/produtos/{id} - Deletar produto
@DeleteMapping("/{id}")
public ResponseEntity<?> deletar(@PathVariable Long id) {
return produtoRepository.findById(id)
.map(produto -> {
produtoRepository.delete(produto);
return ResponseEntity.ok().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
Explicando as anotações:
@RestController
: Marca a classe como um controller REST@RequestMapping
: Define a URL base para todos os endpoints@GetMapping
,@PostMapping
, etc.: Mapeia métodos HTTP específicos@PathVariable
: Captura valores da URL@RequestBody
: Converte JSON em objeto Java automaticamente
4. Configurando o Banco de Dados
Para desenvolvimento, vamos usar o H2 (banco em memória). Adicione no application.properties
:
# Configuração do H2
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Console do H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# JPA/Hibernate
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Testando a API
1. Executando a Aplicação
Execute a classe principal ProjetoApplication.java
ou use o comando:
mvn spring-boot:run
A aplicação iniciará na porta 8080.
2. Testando com Postman ou cURL
Criar um produto:
curl -X POST http://localhost:8080/api/produtos \
-H "Content-Type: application/json" \
-d '{
"nome": "Notebook",
"preco": 2500.00,
"descricao": "Notebook para desenvolvimento"
}'
Listar produtos:
curl http://localhost:8080/api/produtos
Buscar por ID:
curl http://localhost:8080/api/produtos/1
Melhorias e Boas Práticas
1. Validação de Dados
Adicione validações na entidade:
import jakarta.validation.constraints.*;
@Entity
public class Produto {
@NotBlank(message = "Nome é obrigatório")
@Size(min = 2, max = 100, message = "Nome deve ter entre 2 e 100 caracteres")
private String nome;
@NotNull(message = "Preço é obrigatório")
@DecimalMin(value = "0.01", message = "Preço deve ser maior que zero")
private Double preco;
// ...
}
No controller, adicione @Valid
:
@PostMapping
public Produto criar(@Valid @RequestBody Produto produto) {
return produtoRepository.save(produto);
}
2. Tratamento de Exceções
Crie um handler global para exceções:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
}
3. DTOs (Data Transfer Objects)
Para separar a representação interna dos dados da API:
public class ProdutoDTO {
private String nome;
private Double preco;
private String descricao;
// Construtores, getters e setters
}
Próximos Passos
Agora que você tem uma API básica funcionando, considere explorar:
Recursos Avançados
- Spring Security: Para autenticação e autorização
- Spring Data JPA: Consultas mais complexas
- Documentação com Swagger: Para documentar sua API automaticamente
- Testes: Unitários e de integração com Spring Boot Test
Banco de Dados
- PostgreSQL/MySQL: Para ambiente de produção
- Flyway/Liquibase: Para versionamento do banco
- Connection Pool: Para otimizar conexões
Deploy e Monitoramento
- Docker: Para containerização
- Spring Boot Actuator: Para monitoramento
- Logs estruturados: Com Logback
- Profiles: Para diferentes ambientes (dev, test, prod)
Conclusão
O Spring Boot revoluciona o desenvolvimento de APIs REST em Java, oferecendo uma experiência de desenvolvimento rápida e produtiva. Com apenas algumas anotações, criamos uma API completa com operações CRUD, validação e tratamento de erros.
Este é apenas o começo da sua jornada com Spring Boot. O framework oferece uma vasta gama de funcionalidades que podem ser exploradas conforme suas necessidades crescem. Continue praticando, explore a documentação oficial e experimente novos recursos.
Lembre-se: A prática constante é fundamental para dominar qualquer tecnologia. Comece com projetos simples e vá aumentando a complexidade gradualmente.
Gostou deste tutorial? Deixe seu comentário e compartilhe suas dúvidas. No próximo artigo, abordaremos como adicionar segurança à nossa API com Spring Security.
Deixe um comentário