herokuのclojure-getting-startedでJSONを返す

# herokuでclojure appを作る
[公式のGetting Started on Heroku with Clojure](https://devcenter.heroku.com/articles/getting-started-with-clojure)に従う。 ~~英語しんどい。。。しんどい。。。~~





# ローカルで動かす
別に `git push heroku master`でherokuに上げなくても、ローカルで試せる。

これも公式が説明してくれる。 ~~英語に慣れてないので見つけにくい~~
Heroku Dev Center - [Getting Started on Heroku with Clojure: Run the app locally](https://devcenter.heroku.com/articles/getting-started-with-clojure#run-the-app-locally)

僕はspacemacsを使っていたので、 `cider-jack-in`でreplを開いてから公式のように `(require 'clojure-getting-started.web)` => `(def server (clojure-getting-started.web/-main))`した。
ファイルを書き換えてサーバに反映したいときは `Space e b`でバッファごと評価し直すといけた。

herokuに送り込むのも、なんかmagitでcommitした後に `P e` `heroku/master`って選んでいったらできた。なにもわからない。





# JSONを返す
[公式のサンプルコード](https://github.com/heroku/clojure-getting-started)では、プレーンテキストだけを返す簡単な構成となっている（404とかはhtmlを返している模様？）

JSONも返したい。 ~~正直なにもわからない~~


## コード

試行錯誤の末こうなった。 `clojure-getting-started`ディレクトリ下で `project.clj`と src/clojure_getting_started/`web.clj`の2ファイルを書き換える。

```clojure:/project.clj
(defproject clojure-getting-started "1.0.0-SNAPSHOT"
  :description "Demo Clojure web app"
  :url "http://clojure-getting-started.herokuapp.com"
  :license {:name "Eclipse Public License v1.0"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [compojure "1.6.1"]
                 [ring/ring-jetty-adapter "1.7.1"]
                 [ring/ring-json "0.5.0"] ; JSONを扱うために追加
                 [environ "1.1.0"]]
  :min-lein-version "2.0.0"
  :plugins [[environ/environ.lein "0.3.1"]]
  :hooks [environ.leiningen.hooks]
  :uberjar-name "clojure-getting-started-standalone.jar"
  :profiles {:production {:env {:production true}}})
```

```clojure:/src/clojure_getting_started/web.clj
(ns clojure-getting-started.web
  (:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
            [compojure.handler :refer [site]]
            [compojure.route :as route]
            [clojure.java.io :as io]
            [ring.adapter.jetty :as jetty]
            [ring.middleware.json :refer [wrap-json-response]]
            [environ.core :refer [env]]))

(defn splash []
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Hello from Heroku"})

(defn my-json-response []
  {:status 200
   :headers {"Content-Type" "application/json;charset=utf-8"}
   :body {:name "v2okimochi"
          :version 2
          :state "( '・ω・`)"}})

(defroutes app
  (GET "/" []
       (splash))
  (GET "/please-json" []
       (my-json-response)) ; JSONを返す場合
  (ANY "*" []
       (route/not-found (slurp (io/resource "404.html")))))

(defn -main [& [port]]
  (let [port (Integer. (or port (env :port) 5000))]
    (jetty/run-jetty (-> (site app)
                         (wrap-json-response))
                     {:port port :join? false}))) ; JSONを返せる実装

;; For interactive development:
;; (.stop server)
;; (def server (-main))
```


## 差分
増えたところと減ったところ。インデントはゆるして。

```diff:/project.clj
(defproject clojure-getting-started "1.0.0-SNAPSHOT"
  :description "Demo Clojure web app"
  :url "http://clojure-getting-started.herokuapp.com"
  :license {:name "Eclipse Public License v1.0"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [compojure "1.6.1"]
                 [ring/ring-jetty-adapter "1.7.1"]
+                [ring/ring-json "0.5.0"] ; JSONを扱うために追加
                 [environ "1.1.0"]]
  :min-lein-version "2.0.0"
  :plugins [[environ/environ.lein "0.3.1"]]
  :hooks [environ.leiningen.hooks]
  :uberjar-name "clojure-getting-started-standalone.jar"
  :profiles {:production {:env {:production true}}})
```

```diff:/src/clojure_getting_started/web.clj
(ns clojure-getting-started.web
  (:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
            [compojure.handler :refer [site]]
            [compojure.route :as route]
            [clojure.java.io :as io]
            [ring.adapter.jetty :as jetty]
+           [ring.middleware.json :refer [wrap-json-response]]
            [environ.core :refer [env]]))

(defn splash []
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Hello from Heroku"})

+ (defn my-json-response []
+   {:status 200
+    :headers {"Content-Type" "application/json;charset=utf-8"}
+    :body {:name "v2okimochi"
+           :version 2
+           :state "( '・ω・`)"}})

(defroutes app
  (GET "/" []
       (splash))
+ (GET "/please-json" []
+      (my-json-response)) ; JSONを返す場合
  (ANY "*" []
       (route/not-found (slurp (io/resource "404.html")))))

(defn -main [& [port]]
  (let [port (Integer. (or port (env :port) 5000))]
-   (jetty/run-jetty (site #'app) {:port port :join? false}) ; サンプルでの実装
+   (jetty/run-jetty (-> (site app)
+                        (wrap-json-response))
+                    {:port port :join? false}))) ; JSONを返せる実装

;; For interactive development:
;; (.stop server)
;; (def server (-main))
```


## 実際に叩くとこう
ローカルなら`localhost:5000/`で繋がる。ルートならサンプル通りのテキストが返る。
新たに作った `localhost:5000/please-json`にアクセスすると、ちゃんとJSONを返してくれる。

ちなみにこういうAPIサーバっぽいのに接続するときには[postman](https://www.getpostman.com/)使うのおすすめ。結果がわかりやすいしヘッダ情報とかも見られる。

```
{
    "name": "v2okimochi",
    "version": 2,
    "state": "( '・ω・`)"
}
```

なおコードにあった `:headers`は無くてもよろしくやってくれた。でも指定すると安心。

~~JSON返すだけで (調べるのが) たいへん。メモしとかないと書いてるそばからもう既に忘れている。~~





# まとめ
- herokuのclojure-getting-startedコードを拡張してJSON返せるようになった
- ~~なんでこうなるのかわかりてえなあ~~