2020年9月14日

こんにちは! みなさん、API Spec書いてますか?私はお仕事で、OpenAPI(Swagger)形式のYAMLを書いています!

ソースコードとドキュメントを別にしてしまうと、実装途中で仕様変更になったときにyml書き直すのが手間だったり、APIの実装と合ってない箇所があったりなどの問題が発生してしまいます。(そもそもyml書くの辛いし)

プロダクションコードに手を加えることなくプロダクションコードをベースにOpenAPI形式のAPIドキュメントを吐き出すライブラリを探していました。 いくつかそれっぽいライブラリはあるのですが、プロダクトションコードに設定書かないといけなかったり、テストに設定書かないといけないし(テストコードが全部あるわけじゃない)で要望を完全に満たしてもらえませんでした... 最近、完全に要望を満たしてくれる springdoc-openapi というライブラリを見つけましたので紹介します!!!!!

springdoc-openapiとは

springdoc-openapiは、Spring Bootを利用しているプロジェクトで簡単にOpenAPI形式のAPIドキュメントを生成してくれるライブラリです。 Web MVC, WebFluxともに対応しているようです。

サポートしているライブラリ
- Open API 3
- Spring Boot(v1 and v2)
- JSR-303, specifically for @NotNull, @Min, @Max, and @Size.
- Swagger-ui
- OAuth 2

サンプルコード

今回は、Web MVCでのサンプルを紹介します。

API

SakeController.java

@RestController
@RequestMapping("/sakes")
@RequiredArgsConstructor
public class SakeController {

    private final SakeService sakeService;

    @GetMapping
    public Page<SakeResponse> page(@PageableDefault Pageable pageable) {
        return sakeService.page(pageable);
    }

    @GetMapping("list")
    public List<SakeResponse> list() {
        return sakeService.list();
    }

    @GetMapping("{id}")
    public SakeResponse get(@PathVariable Integer id) {
        return sakeService.get(id);
    }

    @PostMapping
    public SakeResponse create(@RequestBody @Validated SakeCreateRequest request) {
        return sakeService.create(request);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity<ErrorResponse> handleException(NotFoundException e) {
        return ResponseEntity.of(Optional.of(new ErrorResponse(e.getMessage())));
    }
}

SakeResponse.java

@Value
public class SakeResponse {
    Integer id;
    String name;
    String brewingName;

    public static SakeResponse newInstance(Sake sake) {
        return new SakeResponse(sake.getId(), sake.getName(), sake.getBrewingName());
    }
}

SakeCreateRequest.java

@Value
public class SakeCreateRequest {
    @NotNull
    @Size(min = 1, max = 20)
    String name;
    @NotNull
    String brewingName;
}

GlobalControllerHandler.java

@RestControllerAdvice
public class GlobalControllerHandler {

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity<ErrorResponse> handleNotFoundException(RuntimeException e) {
        return ResponseEntity.of(Optional.of(new ErrorResponse(e.getMessage())));
    }
}

ただのController周りの実装なので、説明は省略します。

springdoc-openapiの導入