読者です 読者をやめる 読者になる 読者になる

ぺい

大阪の専門学生の落書き。主にエンジニア寄りの話。

Goのgoaを使ったAPIデザインまででイケてるところ

goa goa goaでAPIデザインまでしてみた

GoのWAFであるgoaの最大の特徴であるAPIデザインまでやって、イケてるなと感じた点を挙げてみました。

結論

goaにはDSLというものがあって、それを覚えるのが「最初はだるい」ですが、覚えてしまえば、すぐに追加や修正が出来るのと、最初の時点で設計をしないと開発に入れないので、今まで以上にどういう設計にすべきかを考えれるので、僕のようなすぐに手を動かしちゃう人には良い。

現時点でイケてるなと感じた点

現在、goaは趣味で開発しているアプリのバックグラウンドに使う目的で開発をしています。

セキュリティ周りのロジックが簡単に適用できる

端末の識別に使うtokenなどをHTTPのヘッダーに含めて、それが正しいものをかを毎回チェックしたりするのですが、そのチェックのロジックを全てのエンドポイントで行うわけではなく、必要な時とそうじゃない時の2パターンがありました。
goaでは、そのチェックロジックgあ以下のように簡単に記述することが出来ました。

goa :: Design-first API Generation
examples/security at master · goadesign/examples · GitHub

ヘッダーチェックロジックの定義(標準で用意されている)

var UserAuth = APIKeySecurity("key", func() {
    Description("ユーザートークン")
    Header("X-Authorization")
})

適用する

var _ = Resource("users", func() {
    BasePath("/users")
    Security(UserAuth) <-----/user以下のエンドポイントでヘッダーのチェックをする

        // このエンドポイントは特殊でチェックしたくない。
    Action("tmp account create", func() {
        Routing(
            POST("/tmp"),
        )
        ~~~~~~
        NoSecurity()  <------/user/tmpではチェックしない
        Response(OK, Token)
        Response(BadRequest, ErrorMedia)
    })

        // ▼チェックされる▼
    Action("account create", func() {
        Routing(
            POST("/new"),
        )
        ~~~~~~
        Response(OK, Token)
        Response(Unauthorized)
        Response(BadRequest, ErrorMedia)
    })
    Action("list", func() {
        Routing(
            POST("/"),
        )
        ~~~~~~
        Response(OK, User)
        Response(Unauthorized)
        Response(BadRequest, ErrorMedia)
    })
        // ▲チェックされる▲
})

まあ、便利!wという感じでしたね。他にもデフォルトでチェックのロジックはgoaで用意されており、ごく普通なアプリを開発するには困らなさそうでした。

パラメーターのバリデーションが行える

地味にかったるいバリデーション。ですが、やらない訳にもいかない。しかし、だるい。
goaはとても簡単にこの問題を解決してくれます。

Param("email", String, "メールアドレス", func() {
    // メールのフォーマットになっているかチェック
    Format(goa.FormatEmail)
})
Param("client_version", String, "アプリのバージョン")
Param("platform", String, "OSとバージョン")
Param("identifier", String, "識別子(android:Android_ID, ios:IDFV)", func() {
    // 正規表現
    Pattern("(^[a-z0-9]{16}$|^[a-z0-9\\-]{32}$)")
})
Required("email", "client_version", "platform", "identifier")

他にも文字数チェックやEnum形式で特定の文字列だけを許可したりなど、いい感じにバリデーションしてくれます。 goa :: Design-first API Generation

Swagger食わせるだけでAPIドキュメントが出来上がる

実際に作成したDSLをSwaggerのフォーマットに合ったjsonyamlファイルを作成してくれます。これに関してはすごいの一言でしたね。。。
見たほうが早いので、以下に自分の作成したものの実例を貼っておきます。
Build, Collaborate & Integrate APIs | SwaggerHub
作らないといけないけど、正直面倒なドキュメント。goaは強制的にドキュメント作成されるので、共同開発する上で最高です。

ビジネスロジックに集中が出来る

goagenコマンドを実行して、フォルダ移動を多少必要としますが、ビジネスロジックが全てcontrollerパッケージにまとまります。
※controllerとserverフォルダはデフォルトではありませんが、私の好みで作成してます。

.
├── LICENSE
├── Makefile
├── README.md
├── app
├── client
├── controller <----------ビジネスロジックを書く(雛形は自動生成される)
│   ├── events.go
│   ├── genres.go
│   ├── prefs.go
│   └── users.go
├── design <----------APIでざいんを書く
│   ├── api_definition.go
│   ├── media_types.go
│   ├── resources.go
│   └── security.go
├── glide.lock
├── glide.yaml
├── server
│   ├── app.yaml <-------GAEの設定ファイル
│   └── main.go <-------ミドルウェアの定義など(雛形は自動生成される)
├── swagger
│   ├── swagger.json
│   └── swagger.yaml
├── tool
└── vendor

DSLで定義したセキュリティ周りやバリデーションなどは、自動的に生成されているので、組む必要がありません。そのため、コントローラがビジネスロジックだけを組めばいい状態になり、シンプルで目的がハッキリとしたコードにすることが出来ます。これはいつもごちゃごちゃになりがちな自分にとっては嬉しかったです。

goaは面倒なところもあるけど、結構良さそう

他にもイケてる点はありますが、とりあえず、印象的だった点を挙げてみました。これから実装に入るところなので、最終的な生産性に関して未知数ですが、微妙にだるい点などもいくつかあるので、うまく解消する方法などが見つかったら紹介していきたいと思います。