Fusic Tech Blog

Fusion of Society, IT and Culture

Goaを使ったデザインファーストなAPIサーバ構築
2018/11/13

Goaを使ったデザインファーストなAPIサーバ構築

こんにちは、Fusicの岡嵜です。

ここ最近、フロントエンドにVue.jsを、バックエンドはRuby on RailsでAPIサーバを構築するという開発が続いています。

Ruby on Railsはシステムを構築するには申し分無いWAFですが、個人的にはAPIサーバを構築するには機能過多だと感じています。勿論、Ruby on Rails5でAPIモードが追加され、APIサーバの構築も想定されたユースケースの1つではあるのですが、MVCに縛られずにもっと簡単にAPIサーバを作りたいな、と。

そんな時に、Go言語でAPIサーバを構築できるGoaというフレームワークを知ったので、試しに使ってみました。

Goaとは

Goa Logo

公式サイト github

デザインファーストなmicroserviceフレームワークです。

どういった点がデザインファーストかと言うと、最初にDSLでAPIサーバの仕様や扱うリソース、対応する機能を定義し、これを元にソースコードの大半が自動生成される点です。自動生成されたソースコード中に、リクエストを受け付けた時の処理を記述するだけでAPIサーバが出来上がるので、処理ロジックに集中して開発することができます。

JWT認証等のセキュリティ機能を追加したり、O/Rマッパーを使用することもできます。Go言語なのでコンパイル時の型チェックがあったり、golangのエコシステム(数多くのツールやライブラリ)を活用出来る点も嬉しいところです。

以下コマンドを実行することでGoaをインストールできます。

$ go get -u github.com/goadesign/goa/...

APIサーバを作ってみる

まずは、Workspace(今回は app というディレクトリ)を作成し、 my_app/design/design.go を作成・編集します。このファイルが、後に元にソースコードを自動生成する際のいわば設計図となります。

VSCode

今回はシンプルに User を取得するAPIサーバを作ることにしました。

package design
 
 import (
 . "github.com/goadesign/goa/design"
 . "github.com/goadesign/goa/design/apidsl"
 )
 
 var \_ = API("MyApp", func() {
 Title("My App")
 Description("This is api server for my application.")
 Scheme("http")
 Host("localhost:8080")
 BasePath("/api/v1")
 })
 
 var \_ = Resource("user", func() {
 BasePath("/users")
 DefaultMedia(UserMedia)
 
 Action("list", func() {
 Description("Get all users")
 Routing(GET("/"))
 Response(OK, func() {
 Media(CollectionOf(UserMedia, func() {
 View("default")
 }))
 })
 Response(NotFound)
 })
 
 Action("show", func() {
 Description("Get user by id")
 Routing(GET("/:userID"))
 Params(func() {
 Param("userID", Integer, "User ID")
 })
 Response(OK)
 Response(NotFound)
 })
 })
 
 // UserMedia defines the media type used to render bottles.
 var UserMedia = MediaType("application/vnd.goa.example.user+json", func() {
 Description("A user of my application")
 Attributes(func() {
 Attribute("id", Integer, "Unique user ID")
 Attribute("email", String, "User's email")
 Attribute("last\_name", String, "User's last name")
 Attribute("first\_name", String, "User's first name")
 Required("id", "email", "last\_name", "first\_name")
 })
 View("default", func() {
 Attribute("id")
 Attribute("email")
 Attribute("last\_name")
 Attribute("first\_name")
 })
 })

コマンドを実行して、ソースコードを自動生成します。

$ goagen bootstrap -d github.com/yuuu/my\_app/design

VSCode

自動生成された user.go にリクエスト時の処理を記述します。

package main
 
 import (
 "github.com/goadesign/goa"
 "github.com/yuuu/my\_app/app"
 )
 
 // UserController implements the user resource.
 type UserController struct {
 \*goa.Controller
 }
 
 // NewUserController creates a user controller.
 func NewUserController(service \*goa.Service) \*UserController {
 return &UserController{Controller: service.NewController("UserController")}
 }
 
 var users = []\*app.GoaExampleUser{
 &app.GoaExampleUser{ID: 1, Email: "user1@example.com", LastName: "Fusic", FirstName: "Taro"},
 &app.GoaExampleUser{ID: 2, Email: "user2@example.com", LastName: "Fusic", FirstName: "Jiro"},
 }
 
 // List runs the list action.
 func (c \*UserController) List(ctx \*app.ListUserContext) error {
 // UserController\_List: start\_implement
 
 // Put your logic here
 // 実際にはデータベース等から読み出す
 res := users
 //res := app.GoaExampleUserCollection{}
 return ctx.OK(res)
 // UserController\_List: end\_implement
 }
 
 // Show runs the show action.
 func (c \*UserController) Show(ctx \*app.ShowUserContext) error {
 // UserController\_Show: start\_implement
 
 // Put your logic here
 // 実際にはデータベース等から読み出す
 res := users[ctx.UserID]
 //res := &app.GoaExampleUser{}
 return ctx.OK(res)
 // UserController\_Show: end\_implement
 }

APIサーバへリクエストしてみる

go run してAPIサーバを起動します。

$ go run \*.go

curl でリクエストすると、レスポンスに User の情報がセットされていることがわかります。

$ curl localhost:8080/api/v1/users/
 [{"email":"user1@example.com","first\_name":"Taro","id":1,"last\_name":"Fusic"},{"email":"user2@example.com","first\_name":"Jiro","id":2,"last\_name":"Fusic"}]
 
 $ curl localhost:8080/api/v1/users/0
 {"email":"user1@example.com","first\_name":"Taro","id":1,"last\_name":"Fusic"}
 
 $ curl localhost:8080/api/v1/users/1
 {"email":"user2@example.com","first\_name":"Jiro","id":2,"last\_name":"Fusic"}

今後の展望

今回はデータベース等を使用せずにAPIサーバの動作確認をしていますが、実際にはデータベースに対してCRUD操作する場合がほとんどなのでO/Rマッパーを使うよう設定する必要があります。また、認証機能(JWT等)を使えるようにする必要があります。

Vue.jsと組み合わせて使うことを想定すると、クロスオリジン要求に対応するよう設定することも必要です。

このあたりは、次回解説したいと思います。

yuuu

yuuu

2018年の年明けに組込み畑からやってきた、2児の父 兼 Webエンジニアです。 mockmockの開発・運用を担当しており、組込みエンジニア時代の経験を活かしてデバイスをプログラミングしたり、簡易的なIoTシステムを作ったりしています。主な開発言語はRuby、時々Go。