JWT Token SpringBoot

Building REST APIs with Spring Boot – A Beginner’s Guide

🧑‍💻 Simple Blog API with Spring Boot

In today’s modern web development, REST APIs are everywhere — from your mobile apps to web dashboards and even IoT devices. In this tutorial, we’ll explore what REST APIs are, how Spring Boot helps you build them easily, and how to create a simple blog API using DTOs, Repositories, and Controllerswithout any Service or ServiceImpl layer.


🚀 What is a REST API?

REST stands for Representational State Transfer. It’s an architectural style that defines rules for creating web services, using HTTP methods (GET, POST, PUT, DELETE) to access and manipulate resources.

✅ Step 1: Client Sends a Request

The client (Postman, browser, frontend app) sends an HTTP request:

  • GET /api/posts → to fetch posts
  • POST /api/posts → to create a post
  • DELETE /api/posts/{id} → to delete a post

✅ Step 2: Request Hits REST Controller

Spring Boot maps the request to your controller method:

@GetMapping("/api/posts")
public List<PostDTO> getAllPosts() {
    // logic here
}

✅ Step 3: Controller Handles Everything Directly

Your controller does all the work instead of calling a service class:

  • Converts DTO to Entity (for POST)
  • Calls the Repository
  • Converts Entity to DTO (for GET)
  • Returns the result as JSON

✅ Step 4: Repository Layer Talks to DB

postRepository.findAll();
postRepository.save(post);
postRepository.deleteById(id);

✅ Step 5: JSON Response Returned to Client

Spring Boot automatically serializes your response to JSON and sends it back to the client.


🔁 Example Flow: GET /api/posts

  1. 📨 Client → Sends request GET /api/posts
  2. 🚦 Controller → Calls postRepo.findAll()
  3. 🧠 Controller → Converts entities to PostDTO
  4. 📦 Repository → Fetches from DB
  5. 📤 Controller → Returns JSON response
  6. 🌍 Client → Receives response

SpringBoot API Flow


✅ Key Concepts of REST

  • Stateless: Each request is independent
  • Resource-based: URLs map to resources
  • HTTP Methods:
    • GET: Read data
    • POST: Create data
    • PUT: Update data
    • DELETE: Remove data

📁 Project Structure

E:.
└───blog
    ├───SpringbootBlogApplication.java
    ├───controller
    │   └───api
    │       ├───PostApiController.java
    │       └───CategoryApiController.java
    ├───dto
    │   ├───PostDTO.java
    │   └───CategoryDTO.java
    ├───entity
    │   ├───Post.java
    │   └───Category.java
    ├───repository
    │   ├───PostRepository.java
    │   └───CategoryRepository.java

📇 Step-by-Step Breakdown

✅ 1. Entity Layer  Category.java

package com.acesoftech.blog.entity;

import jakarta.persistence.*;
import java.util.List;

@Entity
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String slug;
    private String description;
    private Long parentId;
    private boolean active;

    @OneToMany(mappedBy = "category")
    private List posts;

    // Getter and Setter for id
    public Long getId() {
        return id;
    }

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

    // Getter and Setter for name
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // Getter and Setter for slug
    public String getSlug() {
        return slug;
    }

    public void setSlug(String slug) {
        this.slug = slug;
    }

    // Getter and Setter for description
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    // Getter and Setter for parentId
    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    // Getter and Setter for active
    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    // Getter and Setter for posts
    public List getPosts() {
        return posts;
    }

    public void setPosts(List posts) {
        this.posts = posts;
    }
}

Post Entity


package com.acesoftech.blog.entity;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.List; // ✅ Imported for List

@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String slug;

    @Column(columnDefinition = "TEXT")
    private String content;

    private boolean featured;

    private boolean active;

    private String imageName;

    private LocalDateTime createdAt = LocalDateTime.now();

    @ManyToOne
    @JoinColumn(name = "category_id")
    private Category category;

    // ✅ ADDED: Relationship to Comment entity
    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
    private List comments;

    // Getters and Setters...

    public Long getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSlug() {
        return slug;
    }

    public void setSlug(String slug) {
        this.slug = slug;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public boolean isFeatured() {
        return featured;
    }

    public void setFeatured(boolean featured) {
        this.featured = featured;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public String getImageName() {
        return imageName;
    }

    public void setImageName(String imageName) {
        this.imageName = imageName;
    }

    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

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

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    // ✅ ADDED: Getter and Setter for comments
    public List getComments() {
        return comments;
    }
 
    public void setComments(List comments) {
        this.comments = comments;
    }
}

 

✅ 2. DTO Layer – PostDTO

public class PostDTO {

    private Long id;
    private String title;
    private String slug;
    private String content;
    private boolean featured;
    private boolean active;
    private String imageName;
    private LocalDateTime createdAt;
    private Long categoryId; // Reference to Category by ID

    // Constructors
    public PostDTO() {}

    public PostDTO(Long id, String title, String slug, String content, boolean featured,
                   boolean active, String imageName, LocalDateTime createdAt, Long categoryId) {
        this.id = id;
        this.title = title;
        this.slug = slug;
        this.content = content;
        this.featured = featured;
        this.active = active;
        this.imageName = imageName;
        this.createdAt = createdAt;
        this.categoryId = categoryId;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }

    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }

    public String getSlug() { return slug; }
    public void setSlug(String slug) { this.slug = slug; }

    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }

    public boolean isFeatured() { return featured; }
    public void setFeatured(boolean featured) { this.featured = featured; }

    public boolean isActive() { return active; }
    public void setActive(boolean active) { this.active = active; }

    public String getImageName() { return imageName; }
    public void setImageName(String imageName) { this.imageName = imageName; }

    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }

    public Long getCategoryId() { return categoryId; }
    public void setCategoryId(Long categoryId) { this.categoryId = categoryId; }
}

✅ Java Code

public class CategoryDTO {

    private Long id;
    private String name;
    private String slug;
    private String description;
    private Long parentId;
    private boolean active;

    // Constructors
    public CategoryDTO() {}

    public CategoryDTO(Long id, String name, String slug, String description, Long parentId, boolean active) {
        this.id = id;
        this.name = name;
        this.slug = slug;
        this.description = description;
        this.parentId = parentId;
        this.active = active;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSlug() {
        return slug;
    }

    public void setSlug(String slug) {
        this.slug = slug;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}

✅ 3. Post Repository Layer

public interface PostRepository extends JpaRepository<Post, Long> {
    List<Post> findByActiveTrueOrderByIdDesc();
}

✅ 3. Category Repository Layer

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
    Category findBySlug(String slug);
}

✅ Controller Code

CategoryApiController.java

@RestController
@RequestMapping("/api/categories")
public class CategoryApiController {

    @Autowired
    private CategoryRepository categoryRepository;

    @GetMapping
    public List<CategoryDTO> getAll() {
        return categoryRepository.findAll().stream()
                .map(cat -> {
                    CategoryDTO dto = new CategoryDTO();
                    dto.setId(cat.getId());
                    dto.setName(cat.getName());
                    return dto;
                }).collect(Collectors.toList());
    }

    @PostMapping
    public CategoryDTO create(@RequestBody CategoryDTO dto) {
        Category category = new Category();
        category.setName(dto.getName());
        Category saved = categoryRepository.save(category);
        dto.setId(saved.getId());
        return dto;
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> delete(@PathVariable Long id) {
        categoryRepository.deleteById(id);
        return ResponseEntity.ok().build();
    }
}


  PostApiController.javaPostController


@RestController
@RequestMapping("/api/posts")
public class PostApiController {

    @Autowired
    private PostRepository postRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    @GetMapping
    public List<PostDTO> getAll() {
        return postRepository.findAll().stream().map(post -> {
            PostDTO dto = new PostDTO();
            dto.setId(post.getId());
            dto.setTitle(post.getTitle());
            dto.setSlug(post.getSlug());
            dto.setContent(post.getContent());
            dto.setActive(post.isActive());
            dto.setFeatured(post.isFeatured());
            dto.setCategoryId(post.getCategory().getId());
            dto.setImageName(post.getImageName());
            dto.setCreatedAt(post.getCreatedAt());
            return dto;
        }).collect(Collectors.toList());
    }

    @PostMapping(consumes = {"multipart/form-data"})
    public ResponseEntity<?> createPostWithImage(
            @RequestPart("post") String postJson,
            @RequestPart(value = "image", required = false) MultipartFile image) {

        ObjectMapper mapper = new ObjectMapper();
        PostDTO dto;
        try {
            dto = mapper.readValue(postJson, PostDTO.class);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("Invalid JSON: " + e.getMessage());
        }

        Category category = categoryRepository.findById(dto.getCategoryId()).orElse(null);
        if (category == null) {
            return ResponseEntity.badRequest().body("Invalid category ID");
        }

        String imageName = null;
        if (image != null && !image.isEmpty()) {
            try {
                imageName = UUID.randomUUID() + "_" + image.getOriginalFilename();
                String uploadDir = System.getProperty("user.dir") + "/uploads/";
                Path uploadPath = Paths.get(uploadDir);
                Files.createDirectories(uploadPath);
                image.transferTo(uploadPath.resolve(imageName).toFile());
            } catch (Exception e) {
                return ResponseEntity.internalServerError().body("Image upload failed: " + e.getMessage());
            }
        }

        Post post = new Post();
        post.setTitle(dto.getTitle());
        post.setSlug(dto.getSlug());
        post.setContent(dto.getContent());
        post.setActive(dto.isActive());
        post.setFeatured(dto.isFeatured());
        post.setImageName(imageName);
        post.setCategory(category);
        post.setCreatedAt(LocalDateTime.now());

        Post saved = postRepository.save(post);

        dto.setId(saved.getId());
        dto.setImageName(imageName);
        dto.setCreatedAt(saved.getCreatedAt());

        return ResponseEntity.ok(dto);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> delete(@PathVariable Long id) {
        postRepository.deleteById(id);
        return ResponseEntity.ok().build();
    }
}

📬 Testing Your API with Postman

  • GET /api/posts → list all posts
  • POST /api/posts → create new post
  • DELETE /api/posts/{id} → delete a post

✅ Final Words

This simplified structure is perfect for beginners or small apps:

  • Controller → handles logic directly
  • Repository → manages data access
  • Entity & DTO → separate database model from API data

Once your app grows, consider adding a service layer to improve modularity and testability.

Leave a Reply

Your email address will not be published. Required fields are marked *