MicronautとKotlinでAPIを作ってOpenAPIドキュメントを生成する

## はじめに

普段は仕事でRuby + SinatraやNode.js + Expressを利用しているのですが、静的型付け言語で良いWEBフレームワークを調べようと思いSpringBootの記事を探していたところ、デブサミでMicronautについて触れられている場面があったので少し触ってみました

## 環境
* macOS Mojave(10.14.2)
* Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
* SDKMAN 5.7.3+337
* Micronaut 1.0.4

## 環境構築

Java、SDKMANはすでに入っている前提

```bash
$ sdk update
$ sdk install micronaut 1.0.4
$ mn create-app my-app --lang kotlin --features swagger-kotlin
```

micronautをインストールし、micronautのCLIコマンドであるmnでスケルトンを構築します
langオプションで言語、featuresオプションで機能を選択できます
今回はKotlinでOpenAPI(Swagger)のドキュメントも出力しようと思います

```
$ cd my-app/
$ ./gradlew run
> Task :run
17:50:53.507 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 751ms. Server Running: http://localhost:8080
```

"./gradlew run"でビルドされ、内包されているhttpサーバが立ち上がります

```bash

$ curl http://localhost:8080
{"_links":{"self":{"href":"/","templated":false}},"message":"Page Not Found"}
```
この時点では何も実装していないのでPage Not Foundとなります

## Validator

build.gradleのdependenciesにvalidatorを追加します

```gradle:my-app/build.gradle
compile "io.micronaut.configuration:micronaut-hibernate-validator"
```

Controllerを作成します

```kotlin:my-app/src/main/kotlin/my/app/HelloController.kt
package my.app

import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.MediaType
import io.micronaut.validation.Validated

import javax.validation.constraints.NotBlank
import javax.validation.constraints.Pattern

@Validated
@Controller("/hello")
class HelloController {

    @Get(
            produces = [MediaType.TEXT_PLAIN],
            uri = "/{name}"
    )
    fun index(@Pattern(regexp = "^t.*") @NotBlank name: String): String {
        return "Hello $name"
    }
}
```

ビルドし、サーバを立ち上げます

```bash
$ ./gradlew run

> Task :run
18:26:06.673 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 772ms. Server Running: http://localhost:8080
<===========--> 85% EXECUTING [7s]
> :run
```

curlで確認します

```
$ curl http://localhost:8080/hello/abc
{"_links":{"self":{"href":"/hello/abc","templated":false}},"message":"name: must match \"^t.*\""}
```

abcだとValidatorで引っかかります


```
$ curl http://localhost:8080/hello/tomiyan
Hello tomiyan
```

tomiyanだとValidatorを通ります

## OpenAPI

アノテーションで説明を書くとドキュメントを生成できます

```kotlin:my/app/Application.kt
package my.app

import io.micronaut.runtime.Micronaut
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.info.Contact
import io.swagger.v3.oas.annotations.info.Info
import io.swagger.v3.oas.annotations.info.License

@OpenAPIDefinition(
        info = Info(
                title = "Hello World",
                version = "0.0",
                description = "My API",
                license = License(name = "MIT", url = "https://example.com"),
                contact = Contact(url = "https://example.com", name = "example", email = "example@example.com")
        )
)
object Application {

    @JvmStatic
    fun main(args: Array<String>) {
        Micronaut.build()
                .packages("my.app")
                .mainClass(Application.javaClass)
                .start()
    }
}
```

```bash
$ ./gradlew run
```

ビルドし、サーバを立ち上げた時点でOpenAPIのYAMLが生成されます

```yaml:my-app/build/tmp/kapt3/classes/main/META-INF/swagger
openapi: 3.0.1
info:
  title: Hello World
  description: My API
  contact:
    name: example
    url: https://example.com
    email: example@example.com
  license:
    name: MIT
    url: https://example.com
  version: "0.0"
paths:
  /hello/{name}:
    get:
      operationId: index
      parameters:
      - name: name
        in: path
        required: true
        schema:
          minLength: 1
          type: string
          format: ^t.*
      responses:
        default:
          content:
            text/plain:
              schema:
                type: string

```

Validatorの内容も含めドキュメント化されています
アノテーションを使えばさらに詳細な説明も追加する事が可能です

## まとめ

ドキュメントもそこそこあり、簡単なAPIを作るには良さそうですね

## 参考文献

* [Micronaut](https://micronaut.io)
* [MicronautでHello World](https://qiita.com/charon/items/e1cdbbf05a08e8d5ea21)