0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Java×SpringBootでTODOアプリのRestAPIを作る

Posted at

はじめに

本記事では、JavaとSpring Bootを使用して、シンプルなTODOアプリのREST APIを作成する方法を紹介します。タスクの作成、更新、削除、取得ができるAPIを実装します。このAPIを作成することで、Spring Bootの基本的な使い方を学び、実際のプロジェクトでよく使われるRESTful APIの設計についても理解を深めることができます。

使用する技術

  • Java 21
  • Spring Boot 3.4.2
  • Gradle
  • H2データベース(軽量で開発用のインメモリデータベース)
  • Lombok

プロジェクトのセットアップ

1. Spring Initializrを使ったプロジェクトの作成

Spring Initializrを使って、基本的なSpring Bootプロジェクトを作成します。

  1. Spring Initializrのサイトにアクセス
    https://start.spring.io/ にアクセスします。

  2. プロジェクトメタデータを設定

    • Project: Gradle Project
    • Language: Java
    • Spring Boot: 3.4.2
    • Project Metadata:
      • Group: com.example
      • Artifact: todo-api
      • Name: todo-api
      • Description: A simple Todo API
      • Package name: com.example.todo
      • Packaging: Jar
      • Java: 21
  3. 依存関係の選択
    以下の依存関係を追加します。

    • Spring Web (REST API作成に必要)
    • Spring Data JPA (JPAを使ってデータベース操作)
    • H2 Database (開発用のインメモリデータベース)
    • Lombok (ボイラープレートコードを減らすため)
  4. プロジェクトの生成
    上記の設定を終えたら、「Generate」ボタンをクリックしてプロジェクトをダウンロードします。

  5. ダウンロードしたファイルを解凍して、IDE(IntelliJやEclipseなど)で開きます

2. プロジェクトの依存関係設定(build.gradle

ダウンロードしたプロジェクトには、build.gradleファイルが自動で生成されています。内容は以下のようになっていますが、念のため確認してください。

build.gradle

plugins {
    id 'org.springframework.boot' version '3.4.2'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '21'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'com.h2database:h2'
    implementation 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

3. application.propertiesの設定

次に、src/main/resources/application.propertiesに、H2データベースの設定を追加します。

src/main/resources/application.properties

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

4. エンティティの作成

次に、TODOタスクを表すTodoエンティティクラスを作成します。

package com.example.todo.entity;

import lombok.Data;

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

@Data
@Entity
public class Todo {

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

    private String title;
    private String description;
    private boolean completed;
}

5. リポジトリの作成

Todoエンティティを操作するためのリポジトリを作成します。

package com.example.todo.repository;

import com.example.todo.entity.Todo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TodoRepository extends JpaRepository<Todo, Long> {
}

6. サービス層の作成

サービス層は、ビジネスロジックを担当します。TODOタスクの作成、更新、削除、取得を担当するメソッドを実装します。

package com.example.todo.service;

import com.example.todo.entity.Todo;
import com.example.todo.repository.TodoRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class TodoService {

    private final TodoRepository todoRepository;

    public TodoService(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    public List<Todo> getAllTodos() {
        return todoRepository.findAll();
    }

    public Optional<Todo> getTodoById(Long id) {
        return todoRepository.findById(id);
    }

    public Todo createTodo(Todo todo) {
        return todoRepository.save(todo);
    }

    public Todo updateTodo(Long id, Todo todo) {
        if (!todoRepository.existsById(id)) {
            return null;
        }
        todo.setId(id);
        return todoRepository.save(todo);
    }

    public boolean deleteTodo(Long id) {
        if (todoRepository.existsById(id)) {
            todoRepository.deleteById(id);
            return true;
        }
        return false;
    }
}

7. コントローラーの作成

次に、REST APIを提供するコントローラーを作成します。

package com.example.todo.controller;

import com.example.todo.entity.Todo;
import com.example.todo.service.TodoService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/todos")
public class TodoController {

    private final TodoService todoService;

    public TodoController(TodoService todoService) {
        this.todoService = todoService;
    }

    @GetMapping
    public List<Todo> getAllTodos() {
        return todoService.getAllTodos();
    }

    @GetMapping("/{id}")
    public Optional<Todo> getTodoById(@PathVariable Long id) {
        return todoService.getTodoById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Todo createTodo(@RequestBody Todo todo) {
        return todoService.createTodo(todo);
    }

    @PutMapping("/{id}")
    public Todo updateTodo(@PathVariable Long id, @RequestBody Todo todo) {
        return todoService.updateTodo(id, todo);
    }

    @DeleteMapping("/{id}")
    public void deleteTodo(@PathVariable Long id) {
        if (!todoService.deleteTodo(id)) {
            throw new RuntimeException("Todo not found");
        }
    }
}

8. 動作確認

Spring Bootアプリケーションが正常に動作することを確認するために、以下の手順で確認します。

8.1 H2コンソールの使用

Spring BootはH2データベースをインメモリで使用しており、H2コンソールを利用してデータベースの内容を確認できます。

  1. application.propertiesファイルに以下の設定があることを確認して、H2コンソールを有効にしていることを確認します。

    spring.h2.console.enabled=true
    spring.h2.console.path=/h2-console
    
  2. アプリケーションを起動します。

  3. ブラウザで http://localhost:8080/h2-console にアクセスします。

  4. データベースのURLを確認します。jdbc:h2:mem:testdb と設定されていれば、そのままでOKです。usernameとpasswordは、application.propertiesに書いている値を記載します。

  5. 「Connect」ボタンをクリックして、H2コンソールに接続します。これで、TODOテーブルの内容が確認できるようになります。

8.2 PostmanでAPIを確認

次に、Postmanやcurlなどを使用して、REST APIの動作を確認します。

  • GET /api/todos: すべてのTODOを取得
  • GET /api/todos/{id}: 指定したIDのTODOを取得
  • POST /api/todos: 新しいTODOを作成
  • PUT /api/todos/{id}: 指定したIDのTODOを更新
  • DELETE /api/todos/{id}: 指定したIDのTODOを削除

各エンドポイントにリクエストを送信して、正常に動作することを確認します。


9. テストの作成

アプリケーションの動作をテストするために、MockMvcを使ってREST APIのテストを行います。MockMvcは、Spring MVCのテスト用に提供されているツールで、実際にサーバーを立ち上げることなく、HTTPリクエストをシミュレーションできます。

9.1 テストクラスの作成

以下のコードで、TODOアプリケーションのREST APIに対するテストを作成します。

package com.example.todo;

import com.example.todo.entity.Todo;
import com.example.todo.repository.TodoRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
public class TodoControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private TodoRepository todoRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @BeforeEach
    public void setUp() {
        todoRepository.deleteAll();
    }

    @Test
    public void testGetAllTodos() throws Exception {
        Todo todo1 = new Todo();
        todo1.setTitle("Test Todo 1");
        todo1.setDescription("Description for test todo 1");
        todo1.setCompleted(false);
        todoRepository.save(todo1);

        mockMvc.perform(get("/api/todos"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Test Todo 1"))
                .andExpect(jsonPath("$[0].description").value("Description for test todo 1"));
    }

    @Test
    public void testCreateTodo() throws Exception {
        Todo todo = new Todo();
        todo.setTitle("New Todo");
        todo.setDescription("This is a new todo");
        todo.setCompleted(false);

        mockMvc.perform(post("/api/todos")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(todo)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.title").value("New Todo"))
                .andExpect(jsonPath("$.description").value("This is a new todo"));

        // DBに新しいTODOが作成されたか確認
        Todo createdTodo = todoRepository.findAll().get(0);
        assertEquals("New Todo", createdTodo.getTitle());
        assertEquals("This is a new todo", createdTodo.getDescription());
    }

    @Test
    public void testUpdateTodo() throws Exception {
        Todo todo = new Todo();
        todo.setTitle("Todo to update");
        todo.setDescription("Description of todo");
        todo.setCompleted(false);
        Todo savedTodo = todoRepository.save(todo);

        savedTodo.setTitle("Updated Todo");
        savedTodo.setDescription("Updated description");

        mockMvc.perform(put("/api/todos/" + savedTodo.getId())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(savedTodo)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.title").value("Updated Todo"))
                .andExpect(jsonPath("$.description").value("Updated description"));

        // DBの値が更新されたことを確認
        Todo updatedTodo = todoRepository.findById(savedTodo.getId()).get();
        assertEquals("Updated Todo", updatedTodo.getTitle());
        assertEquals("Updated description", updatedTodo.getDescription());
    }

    @Test
    public void testDeleteTodo() throws Exception {
        Todo todo = new Todo();
        todo.setTitle("Todo to delete");
        todo.setDescription("Description of todo to delete");
        todo.setCompleted(false);
        Todo savedTodo = todoRepository.save(todo);

        mockMvc.perform(delete("/api/todos/" + savedTodo.getId()))
                .andExpect(status().isOk());

        // DBから削除されていることを確認
        assertTrue(todoRepository.findById(savedTodo.getId()).isEmpty());
    }
}

9.2 テストの説明

  • testGetAllTodos: GET /api/todosエンドポイントをテストします。保存したTODOが返されることを確認します。
  • testCreateTodo: POST /api/todosエンドポイントをテストします。新しいTODOを作成し、レスポンスが正しいことを確認した後、DBに新しいTODOが保存されたことも検証します。
  • testUpdateTodo: PUT /api/todos/{id}エンドポイントをテストします。既存のTODOを更新し、レスポンスに反映されることを確認した後、DBに保存されたTODOの内容が更新されていることも検証します。
  • testDeleteTodo: DELETE /api/todos/{id}エンドポイントをテストします。指定したIDのTODOを削除し、そのTODOがDBから削除されたことを確認します。

9.3 テスト実行方法

依存関係にJUnitを使用している場合、IDEで直接テストを実行するか、以下のコマンドでテストを実行できます。

./gradlew test

結論

この記事では、Java 21とSpring Boot 3.4.2を使用して、シンプルなTODOアプリのREST APIを実装しました。これで、Spring Bootを使ったRESTful APIの基本的な実装方法を理解できたと思います。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?