OpenAPIを使って、Spring Boot製のAPIからアプリ用のDartコードを自動生成する

こんにちは。 エキサイト株式会社の三浦です。

皆さんは、 OpenAPI をご存知でしょうか?

OpenAPIとはAPIのドキュメントを書くときの仕様のことで、使われているプログラミング言語フレームワークに依存しないため、ドキュメントの標準的な仕様として使われています。

このOpenAPI仕様で作ったドキュメントなのですが、実は OpenAPI Generator というツールを使うことで、そのドキュメントに書かれたAPIに対応するコードを自動生成できます。

今回は、OpenAPIとOpenAPI Generatorを使って、Spring Boot製のAPIからアプリ用のDartコードを自動生成する方法を紹介します。

はじめに

OpenAPIとは

OpenAPIとは、端的に言うと「APIのドキュメントの標準化された書き方」です。

www.openapis.org

The OpenAPI Specification is a specification language for HTTP APIs that provides a standardized means to define your API to others.

そのAPIを開発するのに使われたプログラミング言語フレームワークに関係のない、標準化されたドキュメント仕様であるため、様々なところで使われています。

そしてOpenAPI Generatorというツールを使うことで、このOpenAPI仕様で作られたドキュメントから、対応したコードを自動生成することができます。

OpenAPI Generatorとは

OpenAPI Generatorは、OpenAPI仕様で書かれたドキュメントをもとに、様々な言語のコードを自動生成できるツールです。

openapi-generator.tech

自動生成されるコードは、

  • ドキュメントに書かれたAPIにリクエストを送り、レスポンスを受け取る「クライアントコード」
  • ドキュメントに書かれたAPI自体を作る「サーバーコード」
  • 等々…

など様々なタイプのコードを作ることが出来る上、

など自動生成できる言語もかなり多いため、おおよその要望に対応できるはずです。

openapi-generator.tech

つまり、

  1. APIを作成
  2. 何らかの方法で、OpenAPI仕様でAPIのドキュメントを作成
  3. OpenAPI仕様のドキュメントにOpenAPI Generatorを使って、対応するコードを自動生成する

が出来れば、「APIからコードを自動生成する」が達成できるということです。

ではここからは、具体的にどうすればよいかを紹介します。

実装

今回は、「Java / Spring Bootで作られたAPIをもとに、そのAPIにアクセスするDartのクライアントコードを自動生成する」ことを目標にします。

なおDartで作ったコードは、Flutter製アプリで使用する想定です。

OpenAPI仕様のドキュメントの自動生成

まずはAPIから、OpenAPI仕様のドキュメントを作る方法です。

Spring Bootであれば、実際に作られたAPIからドキュメントを自動生成することが可能です。

まずは、OpenAPI仕様のドキュメントを自動生成するためにライブラリを追加します。

今回は build.gradle を使用します。

dependencies {
    implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
}

次に、APIを作っていきます。

今回は非常にシンプルに以下のようにしてみました。

package sample;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("sample")
public class SampleController {

    @GetMapping
    public String sample() {
        return "Hello world!";
    }
}

これで準備は完了です。

実行して、以下のURLにアクセスしてみてください。 (ただしドメインは、自身の実行環境に合わせてください。)

http://localhost/v3/api-docs.yaml

すると、以下のようなYAMLファイルが取得できます。

openapi: 3.0.1
info:
  title: OpenAPI definition
  version: v0
servers:
- url: http://localhost
  description: Generated server url
paths:
  /sample:
    get:
      tags:
      - sample-controller
      operationId: sample
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                type: string
components: {}

なんとこれだけで、OpenAPI仕様のドキュメントが完成しました!

ちなみに、更にドキュメントの精度を上げたい場合は、以下のようにアノテーションを使って情報を補完すると良いでしょう。

package sample;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("sample")
@Tag(name = "サンプルAPI")
public class SampleController {
    @GetMapping
    @Operation(summary = "サンプルエンドポイント")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "正常に処理が終了した場合",
                    content = @Content(mediaType = "application/text", schema = @Schema(implementation = String.class))
            )
    })
    public String sample() {
        return "Hello world!";
    }
}

こうすれば、ドキュメントも以下のように詳細になります。

openapi: 3.0.1
info:
  title: OpenAPI definition
  version: v0
servers:
- url: http://localhost
  description: Generated server url
paths:
  /sample:
    get:
      tags:
      - サンプルAPI
      summary: サンプルエンドポイント
      operationId: sample
      responses:
        "200":
          description: 正常に処理が終了した場合
          content:
            application/text:
              schema:
                type: string
components: {}

OpenAPI Generatorを使ってDartコードを自動生成する

次に、今作ったドキュメントをもとに、OpenAPI Generatorを使ってコードを自動生成します。

OpenAPI Generatorを使う方法は色々とありますが、今回はシンプルにDockerを使っていきます。

openapi-generator.tech

Dockerを使う場合、必要なのは以下の2つのみです。

  • Dockerを実行できる環境
  • OpenAPI仕様で作ったドキュメント

Dockerが実行できる環境で、ドキュメントが存在するディレクトリにて以下を実行してください。

docker run --rm \
  -v "./:/local" \
  openapitools/openapi-generator-cli:v6.6.0 \
  generate \
  -i /local/openapi.yml \
  -g dart-dio \
  -o /local/generated/dart

なんと、これで終わりです!

具体的には以下のようになっています。

docker run --rm \
  -v "./:/local" \ # 実行するときのディレクトリを、コンテナ内の /local というディレクトリと同期する
  openapitools/openapi-generator-cli:v6.6.0 \ # 指定バージョンのOpenAPI GeneratorのCLIのイメージを取得
  generate \ # 自動生成を開始する
  -i /local/openapi.yml \ # 生成元のAPIドキュメント。ここで指定されているパスは、ホスト環境ではなくコンテナ環境のものであることに注意
  -g dart-dio \ # 生成するコードの種類。今回は、Dartのコード(内部でdioというHTTPクライアントを使用)で作成する
  -o /local/generated/dart # 生成したコードを置く先。ここで指定されているパスは、ホスト環境ではなくコンテナ環境のものであることに注意

実行すると、 ./generated/dart ディレクトリが作られ、そこに自動生成されたコードが存在しているはずです。

これで自動生成は完了です。 お疲れ様でした!

最後に

今回は、Flutter製アプリで使うことを想定して、Dartでの自動生成を行いました。

これは、今回これを試そうと思ったきっかけが「アプリエンジニアの負担を減らす」ことだったからです。

世界的に、バックエンドエンジニアに対してアプリエンジニアは数が少ない傾向にあります。

つまり、「アプリの開発速度を上げる」ためには、「アプリエンジニアの負担を減らす」ことが重要なのです。

今回のコード自動生成を使えば、APIとの接続部分だけとはいえ、アプリエンジニアの負担を減らすことが出来るはずです。

ここでは Dart + dio にてコードを自動生成しましたが、それ以外にも生成に対応するコードの種類はたくさんあります。

ぜひ皆さんも試してみてください!