SpringbootでAPIを作成し仕様書をswaggerで自動作成する

# 目的

springbootでOpenAPI（swagger）を導入して簡単なアプリを作成する


- OpenAPIの使い方を学習すること
- APIの操作方法を学ぶこと
- CRUDを自作する


## 開発環境

- ProductName:	macOS
- ProductVersion:	11.3
- IDE: eclipse_2021_03


## REST(REpresentational State Transfer)とは？

- 設計方法のこと
    - **HTTPで定義されているメソッドの使用する**（例：サーバ上にリソースを作成するにはPOST、リソースを取得するにはGET、リソースの状態を変更／更新するにはPUT、削除するにはDELETE）
    - ステートレスであること（サーバーはクライアントのことを一切覚えない）
    - 処理結果をHTTPステータスコードで返す
- 様々な設計原則の集合


## RESTful API(REST API)とは？

- RESTの設計原則に従ったAPI
- RESTful Webサービスとの通信を可能になる

## Swaggerとは？

**SwaggerはOAI(Open API Initiative)が採用しているREST APIを構築するためのオープンソースのフレームワーク**

## なぜSwaggerなのか？

1. OSSで最も使われているAPIドキュメント自動生成ツール
1. 各フレームワークで利用可能
    - Spring MVC
    - Swagger-PHP
    - Ruby用のgrape-swagger
1. Swaggerツールが利用可能
    - Swagger Editor　
        - APIドキュメントの作成、新しいAPIの記述、既に記述したAPIの編集、リアルタイムにフィードバックでSwagger定義のレンダリングをサポート
    - Swagger UI
        - 開発者向けに、複数のプラットフォームに対応した自作のドキュメントを作成。
    - Swagger Codegen
        - 開発者がクライアント用のコード・ライブラリーを作成。サーバー・スタブやSDKを生成することで、より高速なAPIをより早く構築することが可能
    - Swagger Inspection
        - SwaggerがあらゆるAPIのOpenAPIドキュメントを自動生成してテストすることができるツール。また、テストはクラウドに自動保存される



## API完成画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/1859ace0-360a-b87c-68db-a39c28f68b27.png)

## フォルダ構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/e6997a5a-d19d-6779-cf41-e9124523e2fc.png)



## アプリ作成

### 新規springスターター・プロジェクトを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/f940f0e1-4d92-0acd-436f-f955c9ff815a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/355bdfd8-0b14-db6b-7c4f-126e2d331a84.png)


### openapiを追加

```xml:pom.xml
	<dependencies>
        <!dependenciesタグ内に、以下を追加します>
        <!ここから>
		<dependency>
		     <groupId>org.springdoc</groupId>
		     <artifactId>springdoc-openapi-ui</artifactId>
		     <version>1.5.9</version>
		</dependency>
        <!ここまで>
	</dependencies>
```


##### Spring REST Docs と OpenAPIの違いの説明はこちらの記事を参考に

https://www.baeldung.com/spring-rest-docs-vs-openapi



### モデルを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/64c3e78b-5ed4-1e65-f6b3-7c5ee5fd5701.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/fa940eb1-b9d5-3596-4c2d-e95d0fbebd42.png)




```java:/spring-rest3/src/main/java/dev/simpleboot/rest/model/Task.java
package dev.simpleboot.rest.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class Task {
    @Id
    @GeneratedValue
    private Long id;

    @NotBlank
    @Size(max = 255)
    private String name;

    @NotNull
    private Boolean completed = false;
}
```


### リポジトリを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/d4212791-73f6-f80a-f2c5-5647d3b7de1b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/e17a1ca4-ffb4-722a-6411-375c2a57a528.png)

```java:/spring-rest3/src/main/java/dev/simpleboot/rest/repository/TaskRepository.java
package dev.simpleboot.rest.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import dev.simpleboot.rest.model.Task;

public interface TaskRepository extends JpaRepository<Task, Long> {

}
```

### コントローラを作成


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/2ffedcae-3fc8-da87-d3fb-0a0cc5f236be.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/b7cc5fba-b4e6-036a-56b6-05c83617e5d2.png)


```java:
package dev.simpleboot.rest.controller.v1;

import java.util.List;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import dev.simpleboot.rest.model.Task;
import dev.simpleboot.rest.repository.TaskRepository;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;

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

    private final TaskRepository repository;

    @Operation(summary = "タスクを全権取得します")
    @GetMapping("/")

    List<Task> findAll() {
        return repository.findAll();
    }

    @Operation(summary = "タスクを登録します")
    @PostMapping("/")
    Task save(@RequestBody Task task) {
        // taskに値が格納してあるため、そのまま保存ができる
        return repository.save(task);
    }

    @Operation(summary = "タスクを1件取得します")
    @GetMapping("/{id}")
    Task findById(@PathVariable Long id) {
        // idに値が格納してあるので、そのまま検索に利用できます
        return repository.findById(id).get();
    }

    @Operation(summary = "タスクを更新します")
    @PutMapping("/{id}")
    Task save(@RequestBody Task newTask, @PathVariable Long id) {
        return repository.findById(id).map(task -> {
            // 該当IDが存在する場合、更新する
            task.setName(newTask.getName());
            task.setCompleted(newTask.getCompleted());
            return repository.save(task);
        }).orElseGet(() -> {
            // 該当IDが存在しない場合、登録する
            newTask.setId(id);
            return repository.save(newTask);
        });
    }

    @Operation(summary = "タスクを削除します")
    @DeleteMapping("/{id}")
    void deleteById(@PathVariable Long id) {
        repository.deleteById(id);
    }
}
```

#### アプリを起動する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/73d61c98-22be-08d1-15e5-03d66550d940.png)


### ブラウザで localhost:8080/swagger-ui.html にアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/5773fde4-23db-03cb-5f41-6691170bc479.png)

### Swagger UIにタイトルを追加する


```java:/spring-rest3/src/main/java/dev/simpleboot/rest/SpringRest3Application.java
package dev.simpleboot.rest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;

@OpenAPIDefinition(info = @Info(title = "Task API", version = "1.0.0", description = "これはTaskアプリケーションのAPIです"))
@SpringBootApplication
public class SpringRest3Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringRest3Application.class, args);
    }

}
```


### ブラウザで localhost:8080/swagger-ui.html にアクセス


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/4832a3a0-b08c-e92e-9785-ed636032eec2.png)


- ✅ドキュメントの名前が変わっている
- ✅説明が追加されている


まとめ

- OpenAPIを使うにはpom.xmlにspringdoc-openapi-uiを追記する



参考記事（いつもありがとうございます）
---

- [Swaggerとは](https://news.mynavi.jp/itsearch/article/devsoft/2395)
- [5 Reasons We Use Swagger to Build and Document APIs](https://www.jobsity.com/blog/5-reasons-we-use-swagger-build-and-document-apis)

参考リポジトリ

- [tutorials/spring-boot-modules/spring-boot-springdoc/](https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-springdoc)