Skip Navigation
Show nav
Heroku Dev Center
  • Get Started
  • ドキュメント
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
  • ドキュメント
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
View categories

Categories

  • Heroku のアーキテクチャ
    • Dyno (アプリコンテナ)
    • スタック (オペレーティングシステムイメージ)
    • ネットワーキングと DNS
    • プラットフォームポリシー
    • プラットフォームの原則
  • コマンドライン
  • デプロイ
    • Git を使用したデプロイ
    • Docker によるデプロイ
    • デプロイ統合
  • 継続的デリバリー
    • 継続的統合
  • 言語サポート
    • Node.js
    • Ruby
      • Bundler の使用
      • Rails のサポート
    • Python
      • Python でのバックグランドジョブ
      • Django の使用
    • Java
      • Maven の使用
      • Java でのデータベース操作
      • Play Framework の使用
      • Spring Boot の使用
      • Java の高度なトピック
    • PHP
    • Go
      • Go の依存関係管理
    • Scala
    • Clojure
  • データベースとデータ管理
    • Heroku Postgres
      • Postgres の基礎
      • Postgres Getting Started
      • Postgres のパフォーマンス
      • Postgres のデータ転送と保持
      • Postgres の可用性
      • Postgres の特別なトピック
    • Heroku Redis
    • Apache Kafka on Heroku
    • その他のデータストア
  • モニタリングとメトリクス
    • ログ記録
  • アプリのパフォーマンス
  • アドオン
    • すべてのアドオン
  • 共同作業
  • セキュリティ
    • アプリのセキュリティ
    • ID と認証
    • コンプライアンス
  • Heroku Enterprise
    • Private Space
      • インフラストラクチャネットワーキング
    • Enterprise Accounts
    • Enterprise Team
    • Heroku Connect (Salesforce 同期)
      • Heroku Connect の管理
      • Heroku Connect のリファレンス
      • Heroku Connect のトラブルシューティング
    • シングルサインオン (SSO)
  • パターンとベストプラクティス
  • Heroku の拡張
    • Platform API
    • アプリの Webhook
    • Heroku Labs
    • アドオンのビルド
      • アドオン開発のタスク
      • アドオン API
      • アドオンのガイドラインと要件
    • CLI プラグインのビルド
    • 開発ビルドパック
    • Dev Center
  • アカウントと請求
  • トラブルシューティングとサポート
  • Integrating with Salesforce
  • 言語サポート
  • Clojure
  • データベース支援型の Clojure Web アプリケーションの構築

This article was contributed by a member of the Heroku community

Its contents might not always reflect updates to the Heroku platform.

データベース支援型の Clojure Web アプリケーションの構築

日本語 — Switch to English

この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。

最終更新日 2021年03月05日(金)

Table of Contents

  • 前提条件
  • clojure.java.jdbc を使用した PostgreSQL への接続
  • Compojure を使用した Web バインディング
  • Hiccup を使用した HTML テンプレート処理
  • 集大成
  • ローカルでのテスト
  • デプロイ

この記事では、データベース支援型の Clojure Web アプリケーションの作成について説明します。

このアプリは Shouter​ という名前の小さな Twitter クローンで、ユーザーが “シャウト” を入力すると、PostgreSQL データベースに保存され、アプリのフロントページに表示されます。Heroku にデプロイされた Shouter の完成した例を確認するか、完成したソース​を表示することができます。

それでは始めましょう。

前提条件

  • Heroku スターターガイド (Clojure)​ を読み、内容を理解していること。
  • PostgreSQL​ データベースサーバーがローカルでインストールおよび実行されていること。
  • Heroku ユーザーアカウント。 無料ですぐにサインアップできます。

clojure.java.jdbc を使用した PostgreSQL への接続

データベースの永続性は、このサンプルアプリを含め、多くの Web アプリケーションにとって重要です。幸いにも、Clojure で正式サポートされているライブラリには、JDBC​ 標準に沿ったデータベース永続性のための clojure.java.jdbc​ が含まれています。

まず、shouter​ という名前の新しいプロジェクトを Leiningen を使用して作成します。

$ lein new shouter

shouter​ プロジェクトで、Clojure JDBC および PostgreSQL ドライバーの依存関係を追加します。ここで示しているのは、この記事の執筆時点で最新のバージョン番号であることに注意してください。今後、バージョン番号がこれよりも新しくなる可能性がありますが、互換性は保証されていません。

project.clj

(defproject shouter "0.0.2"
  :description "Shouter app"
  :url "https://github.com/technomancy/shouter"
  :min-lein-version "2.0.0"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/java.jdbc "0.6.1"]
                 [org.postgresql/postgresql "9.4-1201-jdbc41"]])

一部のパッケージマネージャでは、デフォルトで PostgreSQL がシステムレベルのバックグラウンドデーモンとして実行されますが、開発作業時は、アクセス許可の問題を回避して可視性を向上させるために、手動で postgres​ を起動することをお勧めします。

$ initdb pg
$ postgres -D pg &

これらの実行可能ファイルが見つからない場合は、/usr/lib/postgresql/*/bin​ を $PATH​ に追加してみてください。

次に、開発作業用のローカル PostgreSQL データベースを作成します。

$ createdb shouter

コマンドラインから、またはエディタ内で REPL を起動して実験を開始します。

$ lein repl

これからまず行うのは、データベースにテーブルを作成することです。JDBC 関数を導入します。

user=> (require '[clojure.java.jdbc :as sql])
nil

これにより、clojure.java.jdbc​ 名前空間が解決され、これから使用する sql​ というエイリアスが設定されます。 次のようにしてテーブルを作成します。

user=> (sql/db-do-commands "postgresql://localhost:5432/shouter"
                           (sql/create-table-ddl :testing [[:data :text]]))
(0)

data​ テキストフィールドを持つ testing​ テーブルがデータベース内に作成されます。data​ フィールドにデータを入れてみましょう。

user=> (sql/insert! "postgresql://localhost:5432/shouter"
                    :testing {:data "Hello World"})
({:data "Hello World"})

適当なデータを作成したので、データベースをクエリしてそのデータを取得してみます。

user=> (sql/query "postgresql://localhost:5432/shouter"
                  ["select * from testing"])
({:data "Hello World"})

上出来です。データは簡単に取得できます。後で邪魔にならないようにテーブルを削除します。

user=> (sql/db-do-commands "postgresql://localhost:5432/shouter"
                           "drop table testing")
(0)

始めるために必要なデータベースの基本は以上です。その他の一般的な疑問点に対する解説は、clojuredocs.org のこちらのガイド​を参照してください。

Compojure を使用した Web バインディング

Compojure​ は、Clojure での Web 開発に広く利用されるライブラリであり、Shouter のコア部分です。

Compojure は、Ruby の Rack​ に似た汎用 Web アプリケーションライブラリの Ring​ をベースに構築されています。Ring はアプリの低レベル接着剤の多くを実装する一方、Compojure はアプリケーションロジックを定義するための簡潔な構文を提供します。

Jetty HTTP サーバーアダプターと共に、Compojure を project.clj​ ファイルに追加します。

project.clj

Leiningen の古いバージョンを使用している場合、更新された依存関係をここで取得するために lein deps​ を手動で実行することが必要な場合があります。

(defproject shouter "0.0.2"
  :description "Shouter app"
  :url "https://github.com/technomancy/shouter"
  :min-lein-version "2.0.0"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/java.jdbc "0.6.1"]
                 [org.postgresql/postgresql "9.4-1201-jdbc41"]
                 [ring/ring-jetty-adapter "1.4.0"]
                 [compojure "1.4.0"]])

ここで、src/shouter/web.clj​ に初期コードを追加します。

src/shouter/web.clj

(ns shouter.web
  (:require [compojure.core :refer [defroutes GET]]
            [ring.adapter.jetty :as ring]))

(defroutes routes
  (GET "/" [] "<h2>Hello World</h2>"))

(defn -main []
  (ring/run-jetty #'routes {:port 8080 :join? false}))

依存関係を変更したので、古い REPL プロセスは失効しています。終了して新しい REPL プロセスを開始してください。

$ lein repl
user> (require 'shouter.web)
nil
user> (shouter.web/-main)
Creating database structure... done
2015-08-26 19:30:07.277:INFO:oejs.Server:nREPL-worker-0: jetty-9.2.10.v20150310
2015-08-26 19:30:07.333:INFO:oejs.ServerConnector:nREPL-worker-0: Started ServerConnector@249942f5{HTTP/1.1}{0.0.0.0:8080}
2015-08-26 19:30:07.334:INFO:oejs.Server:nREPL-worker-0: Started @8655ms
#object[org.eclipse.jetty.server.Server 0x5f92ac2e "org.eclipse.jetty.server.Server@5f92ac2e"]
shouter.web=>

ブラウザで http://localhost:8080​ にアクセスして、作業の成果を確認できるようになりました。次の手順は HTML テンプレートです。

Hiccup を使用した HTML テンプレート処理

Clojure にはさまざまな HTML テンプレートライブラリがありますが、最もシンプルなのは Hiccup​ です。Hiccup テンプレートは、呼び出されると HTML を出力する単なる Clojure 関数です。Hiccup を使用して単純な HTML テンプレート処理をアプリに追加しましょう。

まず、Hiccup を project.clj​ ファイルに追加します。

project.clj

(defproject shouter "0.0.2"
  :description "Shouter app"
  :url "https://github.com/technomancy/shouter"
  :min-lein-version "2.0.0"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/java.jdbc "0.6.1"]
                 [org.postgresql/postgresql "9.4-1201-jdbc41"]
                 [ring/ring-jetty-adapter "1.4.0"]
                 [compojure "1.4.0"]
                 [ring/ring-defaults "0.1.2"]
                 [hiccup "1.0.5"]])

ここで REPL を開始し、Hiccup の動作をざっと見てみます。

user=> (require '[hiccup.core :as h])
nil
user=> (h/html [:h1 "Hello Word"])
"<h1>Hello Word</h1>"

現在の Compojure アプリケーションの内部で Hiccup を使用して、必要なすべての HTML を生成できます。src/shouter/web.clj​ で、hiccup.page​ を ns​ 宣言に追加します。

(:require [hiccup.page :as page])

ここで、単純な index​ 関数を追加します。

(defn index []
  (page/html5
    [:head
      [:title "Hello World"]]
    [:body
      [:div {:id "content"} "Hello World"]]))

最後に、index​ 関数をルートに追加します。ルートは最終的に次のようになります。

(defroutes routes
  (GET "/" [] (index)))

アプリケーションを再起動します。好みに応じて、repl から -main​ を呼び出す代わりに、lein run -m shouter.web​ を使用してサーバーを起動することができます。http://localhost:8080​ にアクセスすると、前回よりも小さなテキストで "Hello World"​ と表示されるはずです。 ソースを確認すると、適切な HTML ドキュメントも生成されているはずです。

Compojure と同様、Hiccup の世界はもっと奥深いものです。ここでは、当面の現実的な目標に向けて、以上の簡単な紹介にとどめています。

集大成

一通りの基本は説明したため、完全な Shouter アプリケーションの構築に進みます。

Shouter アプリケーションの完全なコードは、一連のスタイルが入った resources​ ディレクトリを含め、こちら​で参照できます。 同じルックアンドフィールを得るために、このディレクトリを自由にプロジェクトにコピーしてください。

アプリケーション本体について、src/shouter/web.clj​ から見ていきます。

src/shouter/web.clj

(ns shouter.web
  (:require [compojure.core :refer [defroutes]]
            [ring.adapter.jetty :as ring]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [shouter.controllers.shouts :as shouts]
            [shouter.views.layout :as layout]
            [shouter.models.migration :as schema])
  (:gen-class))

(defroutes routes
  shouts/routes
  (route/resources "/")
  (route/not-found (layout/four-oh-four)))

(def application (wrap-defaults routes site-defaults))

(defn start [port]
  (ring/run-jetty application {:port port
                               :join? false}))

(defn -main []
  (schema/migrate)
  (let [port (Integer. (or (System/getenv "PORT") "8080"))]
    (start port)))

index​ 関数が削除され、いくつかのルートが追加されていることがわかります。新しい -main​ 関数はコマンドラインからアプリケーションを起動するときの処理であり、start​ 関数は REPL 内部から操作するときの処理です。コントローラーの名前空間 shouts が追加されています。コードを入力します。

src/shouter/controllers/shouts.clj

(ns shouter.controllers.shouts
  (:require [compojure.core :refer [defroutes GET POST]]
            [clojure.string :as str]
            [ring.util.response :as ring]
            [shouter.views.shouts :as view]
            [shouter.models.shout :as model]))

(defn index []
  (view/index (model/all)))

(defn create
  [shout]
  (when-not (str/blank? shout)
    (model/create shout))
  (ring/redirect "/"))

(defroutes routes
  (GET  "/" [] (index))
  (POST "/" [shout] (create shout)))

上記のコードは、ユーザーアクションを処理するためのインフラストラクチャです。 下から上に見ていくと、web.clj​ でリンクされたコードの断片である routes が見つかります。ここでは、このコントローラーでのルートの処理方法を記述しています。 この手法を使用すると、参照用にルートを各コントローラーに保持しておき、後から web.clj​ に含まれるグローバルルートで参照することができます。 index​ および create​ 関数は単純な応答処理にすぎません。次に示すのはビューレイヤです。

src/shouter/views/layout.clj

(ns shouter.views.layout
  (:require [hiccup.page :as h]))

(defn common [title & body]
  (h/html5
   [:head
    [:meta {:charset "utf-8"}]
    [:meta {:http-equiv "X-UA-Compatible" :content "IE=edge,chrome=1"}]
    [:meta {:name "viewport" :content
            "width=device-width, initial-scale=1, maximum-scale=1"}]
    [:title title]
    (h/include-css "/stylesheets/base.css"
                 "/stylesheets/skeleton.css"
                 "/stylesheets/screen.css")
    (h/include-css "http://fonts.googleapis.com/css?family=Sigmar+One&v1")]
   [:body
    [:div {:id "header"}
     [:h1 {:class "container"} "SHOUTER"]]
    [:div {:id "content" :class "container"} body]]))

(defn four-oh-four []
  (common "Page Not Found"
          [:div {:id "four-oh-four"}
           "The page you requested could not be found"]))

これは、ビューをレンダリングするための基礎です。 重複を減らすために、表示関数で呼び出せるテンプレートをセットアップします。 four-oh-four​ 関数は、グローバルルートでセットアップされる小さな拡張機能にすぎません。次に、実際のビューに進みます。

src/shouter/views/shouts.clj

(ns shouter.views.shouts
  (:require [shouter.views.layout :as layout]
            [hiccup.core :refer [h]]
            [hiccup.form :as form]
            [ring.util.anti-forgery :as anti-forgery]))

(defn shout-form []
  [:div {:id "shout-form" :class "sixteen columns alpha omega"}
   (form/form-to [:post "/"]
                 (anti-forgery/anti-forgery-field)
                 (form/label "shout" "What do you want to SHOUT?")
                 (form/text-area "shout")
                 (form/submit-button "SHOUT!"))])

(defn display-shouts [shouts]
  [:div {:class "shouts sixteen columns alpha omega"}
   (map
    (fn [shout] [:h2 {:class "shout"} (h (:body shout))])
    shouts)])

(defn index [shouts]
  (layout/common "SHOUTER"
                 (shout-form)
                 [:div {:class "clear"}]
                 (display-shouts shouts)))

ここは表示ロジックの要所です。layout.clj​ にコードが追加されたので見通しが良くなっています。フロントエンドは完成したので、データレイヤに移ります。

src/shouter/models/migration.clj

(ns shouter.models.migration
  (:require [clojure.java.jdbc :as sql]
            [shouter.models.shout :as shout]))

(defn migrated? []
  (-> (sql/query shout/spec
                 [(str "select count(*) from information_schema.tables "
                       "where table_name='shouts'")])
      first :count pos?))

(defn migrate []
  (when (not (migrated?))
    (print "Creating database structure...") (flush)
    (sql/db-do-commands shout/spec
                        (sql/create-table-ddl
                         :shouts
                         [[:id :serial "PRIMARY KEY"]
                         [:body :varchar "NOT NULL"]
                         [:created_at :timestamp
                          "NOT NULL" "DEFAULT CURRENT_TIMESTAMP"]]))
    (println " done")))

次に示すのは、shouts​ テーブルをデータベースに作成して必要なフィールドを定義するコードです。これを実行してシャウトを保存します。

src/shouter/models/shout.clj

(ns shouter.models.shout
  (:require [clojure.java.jdbc :as sql]))

(def spec (or (System/getenv "DATABASE_URL")
              "postgresql://localhost:5432/shouter"))

(defn all []
  (into [] (sql/query spec ["select * from shouts order by id desc"])))

(defn create [shout]
  (sql/insert! spec :shouts [:body] [shout]))

実際のデータモデルは実にシンプルです。 大事なことは入力であり、すべてのシャウトを収集することです。

エントリポイント

“uberjar” がデプロイ用に生成されます。これは単に、アプリのコードとその依存関係のすべてをまとめた jar アーカイブのことです。uberjar の行き先を明確にするために、アプリの project.clj​ の :main​ で、エントリポイントの名前空間を Leiningen に指示する必要があります。

(defproject shouter "0.0.2"
  :description "Shouter app"
  :url "https://github.com/technomancy/shouter"
  :min-lein-version "2.0.0"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/java.jdbc "0.6.1"]
                 [org.postgresql/postgresql "9.4-1201-jdbc41"]
                 [ring/ring-jetty-adapter "1.4.0"]
                 [compojure "1.4.0"]
                 [ring/ring-defaults "0.1.2"]
                 [hiccup "1.0.5"]]
  :main ^:skip-aot shouter.web
  :uberjar-name "shouter-standalone.jar"
  :plugins [[lein-ring "0.8.13"]]
  :ring {:handler shouter.web/application
         :init shouter.models.migration/migrate}
  :profiles {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                                  [ring-mock "0.1.5"]]}
             :uberjar {:aot :all}})

ビルドプロセス中に限って AOT (ahead-of-time: 事前) コンパイルをトリガーするために使用される :uberjar​ プロファイル​もあります。project.clj​ の一番上の階層に :aot :all​ を配置できますが、これによって開発中に問題が起きる可能性があるため、このコンパイルはデプロイ中にのみ実行するのが最善です。最後に Heroku では、Procfile​ において、プラットフォームに依存しない形でエントリポイントを宣言する必要があります。

web: java $JVM_OPTS -jar target/shouter-standalone.jar

これで、アプリケーションが完成しました。

ローカルでのテスト

ローカルで uberjar を生成して、正しく機能することを確認します。

$ lein uberjar
Compiling shouter.web
Compiling shouter.views.shouts
Compiling shouter.views.layout
Compiling shouter.models.shout
Compiling shouter.models.migration
Compiling shouter.controllers.shouts
Created /home/phil/src/shouter/target/shouter-0.0.1.jar
Created /home/phil/src/shouter/target/shouter-standalone.jar

最後に、Procfile​ に定義した内容に従ってアプリケーションを起動します。

$ java $JVM_OPTS -jar target/shouter-standalone.jar
2014-01-20 16:29:23.309:INFO:oejs.Server:jetty-7.x.y-SNAPSHOT
2014-01-20 16:29:23.372:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080

http://localhost:8080​ にアクセスすると、完全な機能を備えた Shouter アプリが実行されているはずです。

デプロイ

すべてが期待どおりに機能することをローカルで確認したので、いよいよアプリを Heroku にデプロイします。

まず、アプリを Git にコミットします。

$ git init
$ git add .
$ git commit -m "init"

次に、Heroku 上でアプリを作成します。

$ heroku create
Creating stormy-fog-408... done, stack is heroku-18
http://stormy-fog-408.herokuapp.com/ | git@heroku.com:stormy-fog-408.git
Git remote heroku added

ここで、アプリ用のデータベースをプロビジョニングする必要があります。以前は、createdb​ コマンドを使用してローカル PostgreSQL データベースをプロビジョニングしました。Heroku では、Heroku PostgreSQL データベースアドオン​を使用してリモートデータベースをプロビジョニングできます。

$ heroku addons:create heroku-postgresql
-----> Adding heroku-postgresql to stormy-fog-408... done, v2 (free)

これにより、DATABASE_URL​ がアプリの環境に追加されます。アプリでは実行時にこれを使用して、そのリモートデータベースリソースに接続します。Heroku Postgres Hobby Tier​ アドオンは無料です。

データベースがプロビジョニングされたので、コードをデプロイして Web dyno をスケールアップできます。

$ git push heroku master
Fetching repository, done.
[...]
To git@heroku.com:stormy-fog-408.git
50084da..4b015a9  master -> master
$ heroku ps:scale web=1
Scaling web processes... done, now running 1

この時点で、アプリケーションが実行されているはずです。次のようにして、Web プロセスが稼働していることを確認します。

$ heroku ps
=== web (Free): `java $JVM_OPTS -jar target/shouter-standalone.jar`
web.1: starting 2015/08/25 10:38:48 (~ 6s ago)

最後に、完成したライブアプリケーションをブラウザで確認してください​。

関連カテゴリー

  • Clojure

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Podcasts
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing

Subscribe to our monthly newsletter

Your email address:

  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Heroku Podcasts
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Facebook
  • Instagram
  • Github
  • LinkedIn
  • YouTube
Heroku is acompany

 © Salesforce.com

  • heroku.com
  • Terms of Service
  • Privacy
  • Cookies
  • Cookie Preferences