俺的【OAS】との向き合い方 (爆速でOpenAPIと友達になろう)

おはようございます。もうすぐ社会人ニ年目に突入のプレッシャーが辛い清田です。😢😢😢😢。
1年間私は開発に携わらせていただきました。Web開発でメンバ全員がOpenAPIを使っていたらもっとスムーズなのではないかと思いエゴ満載の記事です。その中で得たであろう俺的なOpenAPIの向き合い方を紹介致します。
関連記事もどうぞ🐾🐾🐾

e-words.jp

tech-blog.optim.co.jp

OpenAPIとの日常

実装をする際にOpenAPIを利用することで、タイプセーフな実装を実現できます。 主な流れはOpenAPI Specificationで定義を行い、定義をもとにOpenAPI Generatorを利用してソースコードを生成❗
といった流れになります。

A 「新しいアプリを追加するぞ」
               「キー(OAS書きます)」 私
      「キー(ソースコード生成します)」
               「キー(実装始めます)」 

OpenAPI Generatorで使用してきた言語・フレームワーク

  • server
    1. spring
    2. go-gin-server
  • client
    1. golang
    2. python
    3. typescript-axios

あまり数はありませんね。😓😓😓

OpenAPIって何者❓

主に下記2つの総称です。

  • OpenAPI Specification・・・OpenAPI Specification(OAS)とは、REST APIのドキュメントなどを記述する形式のことです。
  • OpenAPI Generator・・・OpenAPI Generatorとは、OpenAPI Specification形式で記述されたYAMLやJSONのファイルをもとにコード生成してくれるツールのことです。

OpenAPI Specification😻

普段YAMLでOASを定義しているため、今回はYAMLで話を進めさせていただきます。😹

swagger.io

github.com

  • OpenAPI Specificationにおいて最低限の記述
    【openapi】, 【info】, 【info.title】, 【info.version】,【paths】これらの記述があれば最低限 OpenAPI Specificationファイルと認識されます。
    OpenAPIを書きたい時はとりあえずこの項目は最低限記述しましょう。✎
openapi: 3.0.0
info:
title: title
version: 1.0.0
paths: ....

pathsについて

pathsとはURLのパス名の部分を表し、パス別にGET, POST, PUT, DELETEなどのREST APIの仕様を記述できます。 itmanabi.com swagger.io

openapi: 3.0.0
info:
  title: title
  version: 1.0.0
paths:
  '/sample/point':
    get:
      tags:
      - api1  #ジェネレートの際のクラスなどの名前になります。  
      operationId: getSamplePoint #ジェネレートの際にメソッドの名前になります。  
      responses: #レスポンスコード + 【description】が最低限必要で、レスポンスの詳細が定義できます。  
          '200':
            description: Pointの取得成功
            content: #レスポンスのスキーマを定義できます。
              text/plain:
                schema:
                  type: string
          '400':
            description: 不正なリクエストです。
          '401':
            description: 認証エラーです。
    post:
      tags:
        - api1
      operationId: postSamplePoint
      requestBody: #PostやPutの際のリクエストボディを定義できます。 
        required: true
        content:
          text/plain:
            schema:
              type: string
      responses:
          '200':
            description: Pointの送信成功

あれ、パスパラメータ・クエリパラメータってどうやって書いたらいいんだ?❓

Describing Parameters | Swagger

openapi: 3.0.0
info:
  title: title
  version: 1.0.0
paths:
  '/sample/point/{param1}':
    get:
      parameters:
        - in: path #ここで 【query】か【path】を指定できます。 query: クエリパラメータ, path: パスパラメータ  
          name: param1 #⚠ パスパラメータで使用する場合。  {param1}のようにパス名で定義した変数名と一致する必要があります。
          schema:
            type: string
        - in: query
          name: param2
          schema:
            type: string
          required: false #⚠ パスパラメータで記述する場合は必ず[required: true]と記述する必要があります。
      responses:
        200:
          description: リクエスト成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  value:
                    type: string

componentsについて

OpenAPI Specificationには【paths】の他にも色々あります。そのなかで次はcomponentsについて紹介させていただきます。

openapi: 3.0.0
info:
  title: title
  version: 1.0.0
paths:
  '/sample/point/{param1}':
    get:
      parameters:
        - $ref: '#/components/parameters/pathParam1' #ファイルの中を参照できます。  
        - $ref: '#/components/parameters/queryParam1'
      responses:
        200:
          description: リクエスト成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SampleResponse'

components:
  parameters: #クエリパラメータ・パスパラメータのコンポーネントを定義できます。  
    pathParam1: #参照の名前なので任意の文字列で問題ありません。  
      in: path
      name: param1
      schema:
        type: string
    queryParam1:
      in: query
      name: param2
      schema:
        type: string
      required: false
  schemas: #コンポーネントを定義できます。
    SampleResponse: #⚠ ジェネレートの際にclass(JAVA)やstruct(Golang)の名前になります。
      type: object
      properties:
        value:
          type: string

OpenAPI Generator🙀

⚠ コマンドはDocker Imageのものを使用します openapi-generator.tech openapi-generator.tech

対応言語・フレームワーク

https://github.com/OpenAPITools/openapi-generator#overview

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200403/20200403145024.png

Spring BootでのOpenAPI Generatorの導入

GitHub - OpenAPITools/openapi-generator: OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)

Maven

github.com

Mavenには下記記載がpom.xmlに必要になります。(依存関係及びジェネレートです) 具体的な参考ソースコード * https://github.com/optim-corp/axios-sample/blob/master/axios-server/pom.xml

<project ....>
  <properties>
    ...
    <swagger-annotations.version>x.x.x</swagger-annotations.version>
    <jackson-databind-nullable.version>x.x.x</jackson-databind-nullable.version>
    <openapi-generator-maven-plugin.version>x.x.x</openapi-generator-maven-plugin.version>
  </properties>
  <dependencies>
    ...
    <dependency>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-annotations</artifactId>
      <version>${swagger-annotations.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openapitools</groupId>
      <artifactId>jackson-databind-nullable</artifactId>
      <version>${jackson-databind-nullable.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.openapitools</groupId>
        <artifactId>openapi-generator-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>ID</id>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <inputSpec>${project.basedir}"OpenAPI yaml or json"</inputSpec>
              <generatorName>spring</generatorName>
              <apiPackage>generate package</apiPackage>
              <modelPackage>generate package</modelPackage>
              <generateSupportingFiles>true</generateSupportingFiles>
              <configOptions>
                ...
              </configOptions>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
          <executions>
            <execution>
              <id>add-source</id>
              <phase>generate-sources</phase>
              <goals>
                <goal>add-source</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <sources>
              <source>${project.build.directory}"generated source"</source>
            </sources>
          </configuration>
      </plugin>
    </plugins>
    </build>
</project>

Gradle

github.com

Gradleには下記の記載がbuild.gradleなどのビルドファイルに必要になります。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.openapitools:openapi-generator-gradle-plugin:x.x.x"
    }
}
plugins {
    id 'org.openapi.generator' version 'x.x.x'
    id 'java'
}
sourceSets {
    main {
        java {
            srcDirs 'src/main/java'
            srcDirs "${rootDir}/build/xxxx"
        }
    }
}

apply plugin: 'org.openapi.generator'
group = 'xxx.xxx'
version = 'x.x.x'
sourceCompatibility = 'xx'
repositories {
    mavenCentral()
}
dependencies {
    implementation 'io.swagger:swagger-annotations:x.x.x'
    compile group: 'org.openapitools', name: 'jackson-databind-nullable', version: 'x.x.x'
}

openApiGenerate {
    generatorName = "spring"
    inputSpec = "${rootDir}OpenAPI yaml or json".toString()
    outputDir = "${rootDir}\\build".toString()
    apiPackage = "xxx.xxx.xxx"
    modelPackage = "xxx.xxx.xxx"
    additionalProperties=[
      ...
    ]
}

compileJava.dependsOn tasks.openApiGenerate

CLIでのOpenAPI Generatorの導入

go gin serverなどのコード生成はMavenやGradleの様なビルドツールによる生成ではなくCLIによる生成になります。

ジェネレートコマンド

Docker Imageの場合

$docker run --rm -v ${PWD}/openapi:/local -v ${PWD}:/project openapitools/openapi-generator-cli generate \
  -i /local/"OpenApi yaml path" \
  -g go-gin-server \
  -o /project/"出力先のディレクトリ"
  -t "マスタッシュのディレクトリ"
  -c "コンフィグファイルのパス(JSONなど)"

他にもNPMやJARなどがあります!!好みで使い分けてください

.openapi-generator-ignoreについて

ソースコードを生成した後にスキーマの変更などがあったりすると思いますが、
その度に毎回ソースコードを書き換えられていたら面倒くさい以外何者でもございません。
そこでこのファイル.openapi-generator-ignoreです。 上書きされたくないファイルを定義出来ます❗ https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#ignore-file-format
書き方は「.gitignore」とほとんど同じです。

サンプル:

github.com

マスタッシュについて

OpenAPIのコード生成結果がコンパイルエラーを起こすバグが存在します。存在しないとも限りません!!
そんな時コード生成結果を変える手段としてマスタッシュを使用することが出来ます。
マスタッシュは意外とにらめっこすれば何が書いてあるのか意外と簡単に理解できます!!!

イメージ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  {{#isString}} // if isString {
    a := '0'    //   a := "0"
  {{/isString}} // }
  {{^isString}} // else {
    a := 0      //   a := 0
  {{/isString}} // }

⚠ バージョンを間違えたマスタッシュを使うと沢山エラーが出る場合があります。

Tips集

コンポーネントだけ生成したい❗

APIのリクエストに関するコード生成以外にもオブジェクトだけ生成したいなぁって時あると思います。
そういう時はpathsを「paths: {}」とすることでAPIの部分が無しで生成できます。

openapi: 3.0.0
info:
title: title
version: 1.0.0
paths: {} <- ここ
components:
  schemas:
    SampleArray:
      type: array
      items:
        $ref: '...'

Docker Compose によるコード生成の効率化❗

docs.docker.jp

CLIによるコード生成はコマンドを複数回実行しないといけなくなるかもしれません。🙀
そのときにDocker Compose を利用して一回のコマンドにしておくとCI/CDや環境構築がシンプルになるかもしれません❗

version: '3'
services:
  generator1:
    image: openapitools/openapi-generator-cli
    command:  generate -i /local/"yaml or json" -g "言語やフレームワーク" -o /project/"出力先"
    volumes:
      - ./"OASファイルのあるディレクトリ":/local
      - .:/project
  generator2:
    image: openapitools/openapi-generator-cli
    command:  generate -i /local/"yaml or json" -g "言語やフレームワーク" -o /project/"出力先"
    volumes:
      - ./"OASファイルのあるディレクトリ":/local
      - .:/project

上記の様なファイルを作って下記のコマンドを実行

$docker-compose up

マスタッシュリポジトリの利用❗

参考: openapi-generator/modules/openapi-generator/src/main/resources at master · OpenAPITools/openapi-generator · GitHub
⚠ 自分の利用しているバージョンに気をつけてマスタッシュを選んでください。エラーが発生するかもしれません🙀

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200403/20200403125225.png

サブモジュールでの管理❗

最近マイクロサービスな設計の開発などが増えていると思います。(個人的な思い込み)
そんな時にGitのサブモジュールの機能を使うことで書くリポジトリにOpenAPIを簡単に共有・導入ができます❗

サンプルコマンド

$git submodule add <URL> <ディレクトリ> -n <サブモジュールの名前>

おすすめのツール

marketplace.visualstudio.com

plugins.jetbrains.com

まとめ✋

「OpenAPIがもっと広まれば良いのに」というエゴで記事を書かさせていただきました。
1年間アプリ開発などに関わらせていただけて、やっと開発の雰囲気がつかめて来た感じです。😓
マイクロサービスに沿った設計の中で、もっと開発の負担がなくなることを願っています。

オプティムはは開発環境を日々改善していきたいと願う戦友エンジニアを募集しています❗

www.optim.co.jp