Spring Boot, Java ekosisteminde en popüler framework’lerden biri. Minimal konfigürasyon ile production-ready uygulamalar geliştirmenizi sağlıyor. Bu yazıda temel bir REST API nasıl oluşturulur, adım adım göreceğiz.
Proje Kurulumu
Spring Initializr kullanarak yeni bir proje oluşturalım. Gerekli bağımlılıklar:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Entity Tanımlama
Önce bir Article entity’si oluşturalım:
package com.example.blog.entity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
@Entity
@Table(name = "articles")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Başlık boş olamaz")
@Size(min = 3, max = 200)
private String title;
@NotBlank(message = "İçerik boş olamaz")
@Column(columnDefinition = "TEXT")
private String content;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
Repository Katmanı
Spring Data JPA ile repository oluşturmak çok basit:
package com.example.blog.repository;
import com.example.blog.entity.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
// Başlığa göre arama
List<Article> findByTitleContainingIgnoreCase(String title);
// En son eklenen makaleler
List<Article> findTop10ByOrderByCreatedAtDesc();
// Özel sorgu
@Query("SELECT a FROM Article a WHERE a.content LIKE %:keyword%")
List<Article> searchByContent(String keyword);
}
Service Katmanı
İş mantığını service katmanında tutuyoruz:
package com.example.blog.service;
import com.example.blog.entity.Article;
import com.example.blog.repository.ArticleRepository;
import com.example.blog.exception.ResourceNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@RequiredArgsConstructor
public class ArticleService {
private final ArticleRepository articleRepository;
public List<Article> getAllArticles() {
return articleRepository.findAll();
}
public Article getArticleById(Long id) {
return articleRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Makale bulunamadı: " + id));
}
@Transactional
public Article createArticle(Article article) {
return articleRepository.save(article);
}
@Transactional
public Article updateArticle(Long id, Article articleDetails) {
Article article = getArticleById(id);
article.setTitle(articleDetails.getTitle());
article.setContent(articleDetails.getContent());
return articleRepository.save(article);
}
@Transactional
public void deleteArticle(Long id) {
Article article = getArticleById(id);
articleRepository.delete(article);
}
public List<Article> searchArticles(String query) {
return articleRepository.findByTitleContainingIgnoreCase(query);
}
}
REST Controller
Son olarak API endpoint’lerini tanımlayalım:
package com.example.blog.controller;
import com.example.blog.entity.Article;
import com.example.blog.service.ArticleService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleService articleService;
@GetMapping
public ResponseEntity<List<Article>> getAllArticles() {
return ResponseEntity.ok(articleService.getAllArticles());
}
@GetMapping("/{id}")
public ResponseEntity<Article> getArticleById(@PathVariable Long id) {
return ResponseEntity.ok(articleService.getArticleById(id));
}
@PostMapping
public ResponseEntity<Article> createArticle(@Valid @RequestBody Article article) {
Article created = articleService.createArticle(article);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{id}")
public ResponseEntity<Article> updateArticle(
@PathVariable Long id,
@Valid @RequestBody Article article) {
return ResponseEntity.ok(articleService.updateArticle(id, article));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteArticle(@PathVariable Long id) {
articleService.deleteArticle(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/search")
public ResponseEntity<List<Article>> searchArticles(@RequestParam String q) {
return ResponseEntity.ok(articleService.searchArticles(q));
}
}
Global Exception Handler
Hataları merkezi bir yerde yönetmek için:
package com.example.blog.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleResourceNotFound(
ResourceNotFoundException ex) {
Map<String, Object> error = new HashMap<>();
error.put("timestamp", LocalDateTime.now());
error.put("message", ex.getMessage());
error.put("status", HttpStatus.NOT_FOUND.value());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationErrors(
MethodArgumentNotValidException ex) {
Map<String, Object> error = new HashMap<>();
Map<String, String> fieldErrors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(fieldError ->
fieldErrors.put(fieldError.getField(), fieldError.getDefaultMessage())
);
error.put("timestamp", LocalDateTime.now());
error.put("errors", fieldErrors);
error.put("status", HttpStatus.BAD_REQUEST.value());
return ResponseEntity.badRequest().body(error);
}
}
Sonuç
Spring Boot ile temiz, katmanlı bir REST API oluşturmak oldukça kolay. Bu yapı sayesinde:
- Bakımı kolay kod yazabilirsiniz
- Test edilebilir bir mimari elde edersiniz
- Ölçeklenebilir uygulamalar geliştirebilirsiniz
Bir sonraki yazıda Spring Security ile JWT authentication eklemeyi göreceğiz.