Spring Bootは、Javaで迅速にWebアプリケーションやREST APIを構築するための強力なフレームワークです。本記事では、Spring Bootプロジェクトの作成から@RestControllerを使ったCRUD(Create, Read, Update, Delete)エンドポイントの実装までを、初心者にもわかりやすく解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Java 17以上
Spring Boot 3.4.x
ビルドツール Maven または Gradle
IDE VS Code(Extension Pack for Javaインストール済み)

事前に以下の準備を完了してください。

  • JDK 17以上のインストール
  • VS Codeと「Extension Pack for Java」および「Spring Boot Extension Pack」のインストール

Spring Initializrでプロジェクトを作成する

Spring Bootプロジェクトの作成には、Spring Initializrを使用します。以下の手順で進めてください。

ブラウザからSpring Initializrを使用する場合

  1. Spring Initializrにアクセスします
  2. 以下の設定を行います
    • Project: Maven(または Gradle)
    • Language: Java
    • Spring Boot: 3.4.x(最新の安定版を選択)
    • Group: com.example
    • Artifact: demo-api
    • Name: demo-api
    • Packaging: Jar
    • Java: 17
  3. Dependenciesで「Spring Web」を追加します
  4. 「Generate」ボタンをクリックしてZIPファイルをダウンロードします
  5. ZIPを解凍し、VS Codeで開きます

VS CodeからSpring Initializrを使用する場合

VS Codeに「Spring Boot Extension Pack」がインストールされている場合、コマンドパレットから直接プロジェクトを生成できます。

  1. Ctrl + Shift + P(macOSはCmd + Shift + P)でコマンドパレットを開きます
  2. 「Spring Initializr: Create a Maven Project」を選択します
  3. Spring Bootのバージョン、言語、グループID、アーティファクトIDを順に設定します
  4. 依存関係で「Spring Web」を選択します
  5. プロジェクトの保存先を指定します

プロジェクト構成の確認

生成されたプロジェクトの基本構成は以下のとおりです。

demo-api/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/demoapi/
│   │   │       └── DemoApiApplication.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       └── java/
│           └── com/example/demoapi/
│               └── DemoApiApplicationTests.java
├── pom.xml(または build.gradle)
└── README.md

pom.xmlには、Spring Webの依存関係が含まれています。

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

REST APIの基本概念

REST APIを実装する前に、Spring MVCの主要なアノテーションを理解しておきましょう。

@RestControllerとは

@RestControllerは、@Controller@ResponseBodyを組み合わせたアノテーションです。このアノテーションが付与されたクラスのメソッドは、戻り値が自動的にJSON形式でHTTPレスポンスボディに変換されます。

HTTPメソッドに対応するマッピングアノテーション

アノテーション HTTPメソッド 用途
@GetMapping GET リソースの取得
@PostMapping POST リソースの作成
@PutMapping PUT リソースの更新
@DeleteMapping DELETE リソースの削除

これらは@RequestMapping(method = RequestMethod.XXX)のショートカットです。

Entityクラスの作成

まず、APIで扱うデータを表現するEntityクラスを作成します。ここでは、タスク管理APIを例にTaskクラスを定義します。

src/main/java/com/example/demoapi/Task.javaを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.demoapi;

public class Task {
    private Long id;
    private String title;
    private boolean completed;

    public Task() {
    }

    public Task(Long id, String title, boolean completed) {
        this.id = id;
        this.title = title;
        this.completed = completed;
    }

    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 boolean isCompleted() {
        return completed;
    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }
}

Java 16以降では、recordクラスを使用してより簡潔に記述することもできます。

1
2
3
4
package com.example.demoapi;

public record Task(Long id, String title, boolean completed) {
}

ただし、recordクラスはイミュータブル(不変)であるため、更新処理には注意が必要です。本記事では、従来のPOJOクラスを使用します。

Controllerクラスの作成

次に、REST APIのエンドポイントを定義するTaskControllerクラスを作成します。

src/main/java/com/example/demoapi/TaskController.javaを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.example.demoapi;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/tasks")
public class TaskController {

    private final List<Task> tasks = new ArrayList<>();
    private final AtomicLong counter = new AtomicLong();

    // 全件取得(GET /api/tasks)
    @GetMapping
    public List<Task> getAllTasks() {
        return tasks;
    }

    // 1件取得(GET /api/tasks/{id})
    @GetMapping("/{id}")
    public ResponseEntity<Task> getTaskById(@PathVariable Long id) {
        Optional<Task> task = tasks.stream()
                .filter(t -> t.getId().equals(id))
                .findFirst();

        return task.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    // 新規作成(POST /api/tasks)
    @PostMapping
    public ResponseEntity<Task> createTask(@RequestBody Task task) {
        task.setId(counter.incrementAndGet());
        tasks.add(task);
        return ResponseEntity.status(HttpStatus.CREATED).body(task);
    }

    // 更新(PUT /api/tasks/{id})
    @PutMapping("/{id}")
    public ResponseEntity<Task> updateTask(@PathVariable Long id, @RequestBody Task updatedTask) {
        for (int i = 0; i < tasks.size(); i++) {
            Task task = tasks.get(i);
            if (task.getId().equals(id)) {
                updatedTask.setId(id);
                tasks.set(i, updatedTask);
                return ResponseEntity.ok(updatedTask);
            }
        }
        return ResponseEntity.notFound().build();
    }

    // 削除(DELETE /api/tasks/{id})
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteTask(@PathVariable Long id) {
        boolean removed = tasks.removeIf(task -> task.getId().equals(id));

        if (removed) {
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}

各アノテーションの詳細解説

上記のコードで使用しているアノテーションを詳しく解説します。

@RestController

クラスレベルに付与し、このクラスがRESTコントローラであることを示します。すべてのメソッドの戻り値は自動的にJSONに変換されます。

@RequestMapping

クラスレベルで使用することで、すべてのエンドポイントの共通パスを定義できます。この例では/api/tasksが共通パスとなります。

@GetMapping

GETリクエストを処理するメソッドに付与します。@GetMapping("/{id}")のようにパスパラメータを指定することで、URLの一部を変数として受け取れます。

@PostMapping

POSTリクエストを処理するメソッドに付与します。リクエストボディのJSONデータを@RequestBodyでJavaオブジェクトに変換して受け取ります。

@PutMapping

PUTリクエストを処理するメソッドに付与します。既存リソースの更新に使用します。

@DeleteMapping

DELETEリクエストを処理するメソッドに付与します。リソースの削除に使用します。

@PathVariable

URLパスの一部を変数として取得するために使用します。/{id}id部分を引数で受け取ることができます。

@RequestBody

HTTPリクエストボディのJSONを指定したJavaオブジェクトにデシリアライズします。

ResponseEntity

HTTPレスポンスのステータスコード、ヘッダー、ボディを細かく制御できるクラスです。

アプリケーションの実行

アプリケーションを起動する方法を説明します。

VS Codeからの実行

  1. DemoApiApplication.javaファイルを開きます
  2. mainメソッドの上に表示される「Run」をクリックします
  3. または、F5キーを押してデバッグ実行します

コマンドラインからの実行

Mavenを使用する場合は、以下のコマンドを実行します。

1
./mvnw spring-boot:run

Gradleを使用する場合は、以下のコマンドを実行します。

1
./gradlew bootRun

起動に成功すると、コンソールに以下のようなログが表示されます。

Started DemoApiApplication in 2.345 seconds

デフォルトでは、アプリケーションはhttp://localhost:8080で起動します。

curlでの動作確認

curlコマンドを使用して、各エンドポイントの動作を確認しましょう。

タスクの作成(POST)

1
2
3
curl -X POST http://localhost:8080/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title": "Spring Bootを学ぶ", "completed": false}'

期待されるレスポンス(HTTPステータス: 201 Created):

1
2
3
4
5
{
  "id": 1,
  "title": "Spring Bootを学ぶ",
  "completed": false
}

全タスクの取得(GET)

1
curl http://localhost:8080/api/tasks

期待されるレスポンス(HTTPステータス: 200 OK):

1
2
3
4
5
6
7
[
  {
    "id": 1,
    "title": "Spring Bootを学ぶ",
    "completed": false
  }
]

特定タスクの取得(GET)

1
curl http://localhost:8080/api/tasks/1

期待されるレスポンス(HTTPステータス: 200 OK):

1
2
3
4
5
{
  "id": 1,
  "title": "Spring Bootを学ぶ",
  "completed": false
}

存在しないIDを指定した場合のレスポンス(HTTPステータス: 404 Not Found):

1
curl http://localhost:8080/api/tasks/999

レスポンスボディは空です。

タスクの更新(PUT)

1
2
3
curl -X PUT http://localhost:8080/api/tasks/1 \
  -H "Content-Type: application/json" \
  -d '{"title": "Spring Bootをマスターする", "completed": true}'

期待されるレスポンス(HTTPステータス: 200 OK):

1
2
3
4
5
{
  "id": 1,
  "title": "Spring Bootをマスターする",
  "completed": true
}

タスクの削除(DELETE)

1
curl -X DELETE http://localhost:8080/api/tasks/1

期待されるレスポンス(HTTPステータス: 204 No Content):

レスポンスボディは空です。

Postmanでの動作確認

GUIツールを好む場合は、Postmanを使用して動作確認ができます。

Postmanでのリクエスト手順

  1. Postmanを起動し、新しいリクエストを作成します
  2. HTTPメソッドを選択します(GET、POST、PUT、DELETE)
  3. URLにhttp://localhost:8080/api/tasksを入力します
  4. POSTやPUTの場合は、「Body」タブで「raw」を選択し、「JSON」を指定してリクエストボディを入力します
  5. 「Send」ボタンをクリックしてリクエストを送信します

REST APIエンドポイントのまとめ

本記事で実装したエンドポイントを図で表すと以下のようになります。

flowchart LR
    subgraph Client
        A[HTTPクライアント]
    end
    
    subgraph "Spring Boot Application"
        B["@RestController<br/>TaskController"]
        
        subgraph Endpoints
            C["GET /api/tasks<br/>全件取得"]
            D["GET /api/tasks/{id}<br/>1件取得"]
            E["POST /api/tasks<br/>新規作成"]
            F["PUT /api/tasks/{id}<br/>更新"]
            G["DELETE /api/tasks/{id}<br/>削除"]
        end
    end
    
    A --> B
    B --> C
    B --> D
    B --> E
    B --> F
    B --> G

各エンドポイントとHTTPステータスコードの対応は以下のとおりです。

メソッド パス 処理内容 成功時ステータス
GET /api/tasks 全タスク取得 200 OK
GET /api/tasks/{id} 特定タスク取得 200 OK / 404 Not Found
POST /api/tasks タスク作成 201 Created
PUT /api/tasks/{id} タスク更新 200 OK / 404 Not Found
DELETE /api/tasks/{id} タスク削除 204 No Content / 404 Not Found

まとめ

本記事では、Spring Bootを使用してREST APIのCRUDエンドポイントを実装する方法を解説しました。

学習したポイントを振り返ります。

  • Spring Initializrを使用したプロジェクトの作成方法
  • @RestControllerによるRESTコントローラの定義
  • @GetMapping@PostMapping@PutMapping@DeleteMappingによるHTTPメソッドのマッピング
  • @PathVariable@RequestBodyによるリクエストデータの取得
  • ResponseEntityによるHTTPレスポンスの制御
  • curlやPostmanを使用した動作確認方法

次のステップとして、以下のトピックを学習することをお勧めします。

  • リクエストパラメータの扱い方(@RequestParam@PathVariableの詳細)
  • Bean Validationによる入力検証
  • Spring Data JPAによるデータベース連携
  • 例外ハンドリングとエラーレスポンスの設計

参考リンク