Elixir+PhoenixでGCMのAPIを叩いてPush通知を送る簡単なAPIをつくってみた

# はじめに
タイトルのままですが、Elixir+PhoenixでGCMサーバーのAPIを叩いてPush通知を送る簡単なWebAPIをつくってみました。

## 経緯
僕自身は普段CakePHPで開発することが多いのですが、最近はスマホアプリ全盛（あとSPA。今後はIoT？）でMVCなWebアプリを開発する機会が減ってきていて、サーバー側はRestなWebAPIを開発することが多くなってきています。
そうするとCakePHPはフルスタック過ぎるし、もっと高性能なAPIサーバーつくりたいなと思いながらもGolangは乗り遅れちゃったし、Node.jsはたまに使うけど個人的に肌なじみがあまりよくなく、何かイケてる言語ないかなぁと悶々としていたところ、@naoya@githubさんが紹介していたスライドをみて興味を持ちました。
言語的な特徴には触れない（他の方が語っていること以外は語れない）ので、他の方の投稿を参照してください。

# 環境構築

## インストール on Mac
brewで簡単に入れられます。

```
$ brew install elixir
```

## Hello, world!
iexコマンドでコマンドプロンプトで対話的に実行出来ます。
rubyのirbコマンドと同じです。

```
$ iex
```

```ex:
iex(1)> IO.puts "Hello, world!"
Hello, world
:ok 
```
ここまでは楽勝。

## Phoenixインストール　

現状最もメジャーなフレームワークであるPhoenixをインストールします。
APIサーバーつくりたいだけなので、Railsライクなのはややオーバースペックな気もしますが、馴染みやすいので入り口としてはよいかなと思っています。

```
$ mix archive.install https://github.com/phoenixframework/phoenix/releases/download/v1.0.3/phoenix_new-1.0.3.ez
``` 

## アプリ作成 / 起動

Phoenixアプリをつくります。
mixコマンドはRailsで言うrailsコマンドです。

```
$ mix phoenix.new gcm_app
```

サーバー起動もRails的な感じ。
アプリのルートディレクトリに移動してから下記コマンドを実行します。

```
$ mix phoenix.server
Unchecked dependencies for environment dev:
* phoenix_live_reload (Hex package)
  the dependency is not available, run `mix deps.get`
* cowboy (Hex package)
  the dependency is not available, run `mix deps.get`
* phoenix_html (Hex package)
  the dependency is not available, run `mix deps.get`
* phoenix (Hex package)
  the dependency is not available, run `mix deps.get`
* postgrex (Hex package)
  the dependency is not available, run `mix deps.get`
* phoenix_ecto (Hex package)
  the dependency is not available, run `mix deps.get`
** (Mix) Can't continue due to errors on dependencies
```

依存パッケージあたりでエラーになってしまった。。
言われた通りに以下を実行します。

```
$ mix deps.get
```

依存パッケージのチェックをしてくれる模様。
再度サーバー起動してみると、成功したっぽい。

http://localhost:4000/

上記URLでWelocome to Phoenix!なページが表示されます。

<img width="752" alt="phoenix_20151105.png" src="https://qiita-image-store.s3.amazonaws.com/0/48990/36e508a3-85a1-46d4-b8b6-f306cdb46307.png">

# API開発

## HTTPクライアントインストール

HTTPクライアントであるHTTPoisonを入れます。
~mix.exsのdepsの箇所にHTTPoisonの設定を追加します。

```ex:mix.exs
  defp deps do
    [{:phoenix, "~> 1.0.3"},
     {:phoenix_ecto, "~> 1.1"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.1"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:cowboy, "~> 1.0"},
     {:httpoison, "~> 0.7.2"}]
  end
```

そして、依存パッケージインストール。

```
$ mix deps.get
```

インストール後、~mix.exsのapplicationの箇所にもHTTPoisonの設定を追加します。
これで実行時HTTPoison.startが不要になるっぽい。

```ex:mix.exs
  def application do
    [mod: {GcmApp, []},
     applications: [:phoenix, :phoenix_html, :cowboy, :logger,
                    :phoenix_ecto, :postgrex, :httpoison]]
  end
```

## ルーター設定

デフォルトのAPI用のルーター設定はコメントアウトされているので、以下のように修正します。

```ex:router.ex
  scope "/api", GcmApp do
    pipe_through :api

    get "/push/:message/", PageController, :push
  end
```

## メイン処理

~web/controllers/page_controller.exにpushメソッドを以下のように追加します。
GCMの仕様については省略。

```ex:page_controller.ex
  def push(conn, %{"message" => message}) do

    url = "https://android.googleapis.com/gcm/send"
    api_key = "********"
    registraion_ids = ["hogehogehogehoge"]

    headers = [{"Content-Type", "application/json;charset=UTF-8"},
               {"Authorization", "key=#{api_key}"}]
    json_data = %{collapse_key: "ff1", 
                  delay_while_idle: true, 
                  time_to_live: 864000,
                  data: %{title: "gcm_app", message: message},
                  registration_ids: registraion_ids} |> Poison.encode!

    # HTTPoison.start # 不要だった
    response = HTTPoison.post!(url,  json_data, headers)
    # IO.puts response.body # デバッグ用

    case response do
      %{status_code: 200, body: body} -> json conn, Poison.decode!(body)
      %{status_code: code} -> json conn, %{error: code}
    end

  end
```

## API実行

開発が完了したら、サーバーを起動してAPIを実行！
成功した場合、下記のようなレスポンスが返ってきます。

```
$ curl http://localhost:4000/api/push/finalfantasy
{"success":1,"results":[{"message_id":"0:1446717643350281%3207dc8500018b71"}],"multicast_id":9177386917145341445,"failure":0,"canonical_ids":0}
```

そして、Androidアプリに"finalfantasy"というPushメッセージが通知されます。

## テスト

ユニットテストについては次の機会に←

# まとめ
Elixir（と言うか新しい言語）たのしい！
でも、まずは動かしてみたレベルなので、まだまだ文法とか内部構造とか理解していないので、これから勉強していこう。ベンチマークもとりたいな。

## 参考文献 / 参考URL

Elixir ご紹介：
https://speakerdeck.com/naoya/elixir-goshao-jie
Installing Elixir：
http://elixir-lang.org/install.html
Elixir 基礎文法最速マスター：
http://qiita.com/niku/items/729ece76d78057b58271
Elixir のパターンマッチを攻略しよう：
http://qiita.com/naoya@github/items/9da982febe89d83cb5b5
Installation · Phoenix：
http://www.phoenixframework.org/docs/installation
Phoenix アプリケーションを作成する：
http://qiita.com/mserizawa/items/a861b4aa0d55b42b2324
edgurgel/httpoison：
https://github.com/edgurgel/httpoison
ElixirのHTTPoisonライブラリの使い方：
http://qiita.com/ColdFreak/items/4beb6cacfbb39baa955f
Phoenix で外部 API にリクエストする API を作る：
http://qiita.com/mserizawa/items/3ac483aca4f2926fa1a9

『WEB+DB PRESS Vol.88』（技術評論社）：
http://gihyo.jp/magazine/wdpress/archive/2015/vol88