おはようございます。もうすぐ社会人ニ年目に突入のプレッシャーが辛い清田です。😢😢😢😢。
1年間私は開発に携わらせていただきました。Web開発でメンバ全員がOpenAPIを使っていたらもっとスムーズなのではないかと思いエゴ満載の記事です。その中で得たであろう俺的なOpenAPIの向き合い方を紹介致します。
関連記事もどうぞ🐾🐾🐾
OpenAPIとの日常
実装をする際にOpenAPIを利用することで、タイプセーフな実装を実現できます。
主な流れはOpenAPI Specificationで定義を行い、定義をもとにOpenAPI Generatorを利用してソースコードを生成❗
といった流れになります。
A 「新しいアプリを追加するぞ」 「キー(OAS書きます)」 私 「キー(ソースコード生成します)」 「キー(実装始めます)」
OpenAPI Generatorで使用してきた言語・フレームワーク
- server
- spring
- go-gin-server
- client
- golang
- python
- typescript-axios
あまり数はありませんね。😓😓😓
OpenAPIって何者❓
主に下記2つの総称です。
- OpenAPI Specification・・・OpenAPI Specification(OAS)とは、REST APIのドキュメントなどを記述する形式のことです。
- OpenAPI Generator・・・OpenAPI Generatorとは、OpenAPI Specification形式で記述されたYAMLやJSONのファイルをもとにコード生成してくれるツールのことです。
OpenAPI Specification😻
普段YAMLでOASを定義しているため、今回はYAMLで話を進めさせていただきます。😹
- 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の送信成功
あれ、パスパラメータ・クエリパラメータってどうやって書いたらいいんだ?❓
例
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
Spring BootでのOpenAPI Generatorの導入
Maven
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
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などがあります!!好みで使い分けてください
- jar - Central Repository: org/openapitools/openapi-generator-cli/4.3.0
- npm - @openapitools/openapi-generator-cli - npm
- 公式dockerイメージ - Docker Hub
.openapi-generator-ignoreについて
ソースコードを生成した後にスキーマの変更などがあったりすると思いますが、
その度に毎回ソースコードを書き換えられていたら面倒くさい以外何者でもございません。
そこでこのファイル.openapi-generator-ignoreです。 上書きされたくないファイルを定義出来ます❗
https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#ignore-file-format
書き方は「.gitignore」とほとんど同じです。
サンプル:
マスタッシュについて
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 によるコード生成の効率化❗
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
⚠ 自分の利用しているバージョンに気をつけてマスタッシュを選んでください。エラーが発生するかもしれません🙀
サブモジュールでの管理❗
最近マイクロサービスな設計の開発などが増えていると思います。(個人的な思い込み)
そんな時にGitのサブモジュールの機能を使うことで書くリポジトリにOpenAPIを簡単に共有・導入ができます❗
サンプルコマンド
$git submodule add <URL> <ディレクトリ> -n <サブモジュールの名前>
おすすめのツール
まとめ✋
「OpenAPIがもっと広まれば良いのに」というエゴで記事を書かさせていただきました。
1年間アプリ開発などに関わらせていただけて、やっと開発の雰囲気がつかめて来た感じです。😓
マイクロサービスに沿った設計の中で、もっと開発の負担がなくなることを願っています。
オプティムはは開発環境を日々改善していきたいと願う戦友エンジニアを募集しています❗