-shared-img-thumb-PAK150307492097_TP_V

Swift+KituraでPostgreSQLを使ったREST APIサンプルを作りました

1週間ほど、空いた時間にKituraというかサーバサイドSwiftを触ってみて、PostgreSQLと連携してデータをinsertしたりselectしたりできるようになったので、メモ。

Fluent

1bdfb1d0-c913-11e5-9b45-f7a6f7cba720

Swiftでデータベースを扱うのにいいライブラリないかな?と探していて見つけたのが、このFluentです。

ただ、この中のSQLクラスがテーブル名などをバッククォートで囲んでしまうため、そのままではPostgreSQLでは使えませんでした・・。

ということで、forkしてクォート文字を設定できるようにしてみました。
https://github.com/d-abe/fluent

同様に、fluentのPostgreSQLドライバも合わせてforkしています。
https://github.com/d-abe/fluent-postgresql

Swift

今回は、Swiftの2/25版を使う前提として考えています。

これを使うと、swift buildに --fetch (Package.swift記載のパッケージ取得のみを行う)とか -Xswiftc (これを使うとswiftcにパラメータを受け渡せるようになり、Kituraを使ったというかCの混ざったプロジェクトでエラーが出なくなる)とかが追加されています。

ファイル構成

今回作ったファイルは以下のような構成になっています。

├── Database
│   └── sample.sql
├── Makefile
├── Package.swift
└── Sources
    └── KituraSample
        ├── Common
        │   └── Constants.swift
        ├── Controllers
        │   └── ArticleController.swift
        ├── Filters
        │   ├── FluentFilter.swift
        │   └── HeaderFilter.swift
        ├── Models
        │   ├── Article.swift
        │   └── ModelExtension.swift
        ├── Routes
        │   └── MyRouter.swift
        └── main.swift

これは以下のリポジトリに一式置いています。
https://github.com/d-abe/kitura_sample

ファイル名 説明
Database/sample.sql テーブル定義のSQLファイル
Makefile Makefile
Package.swift Swift Package Managerのパッケージ定義のソース
Common/Constants.swift 共通定数クラス
Controllers/ArticleController.swift Article関連コントローラクラス
Filters/FluentFilter.swift fluent初期化(データベース接続)フィルタ
Filters/HeaderFilter.swift レスポンスヘッダ設定フィルタ
Models/Article.swift Articleモデルクラス
Models/ModelExtension.swift ModelにNSDateやIntのstring相互変換メソッドを追加
Routes/MyRouter.swift ルーティング定義クラス
main.swift エントリポイント

では、それぞれについて見ていきます。

エントリポイント

プログラムを実行すると呼び出されるのがこのmain.swiftになります。

ルーティングクラスをインスタンス化してHttpServerに渡して(ポート8090で)起動させているだけですね。

この中のLithiumLoggerというのはHeliumLoggerをログレベルの設定ができるようにしたもので、ここではそのレベルを設定しています。

ルーティング

ルーティングのMyRouterクラスは以下の通りです。

これを見ると、useでミドルウェアを渡せるのがわかりますが、その前にURLのパターンを設定できるので全体に対してもできるし/member内だけとかグルーピングして設定することもできます。

これによって、共通のDB接続とかは全体、認証関連は /member以下、のようなことができます。
ミドルウェア適用の順序が保証されるかどうかは不明なので、順序に依存しないような構造にすべきですね。

あとは、GETの時はArticleController.getAll()、POSTの時はArticleController.add()を呼び出すようにしています。

コントローラとモデル

では、コントローラクラスでは何をしているのか見てみます。

まず、

let json = JSON(Article.findAll().toJSON())

ここで、Article.findAll()を実行してデータベースから全件取得して結果をJSONに変換しています。

Articleクラス

モデルクラスのArticleを見ておきましょう。

大したことはしていません。プロパティと [String:String] の相互変換と、staticメソッドのfindAll()を定義しているだけです。

モデルに関しては、

  • プロパティの定義
  • serialize/initの定義
  • staticメソッドの実装

をどんどん行う感じになるかと思います。

ArticleController.add()

コントローラに戻って、add()メソッドを見てみます。

if let body = request.body {

  if let json = body.asJson() {

この部分ですが、ルータ内で設定していたBodyParserというミドルウェアが、Content-Typeの値によって自動的にrequest.bodyに値がセットして、(今回はapplication/jsonのContent-Typeで渡す前提なので)body.asJson()を使ってJSONの値を取得しています。

データのINSERT

INSERTも簡単です。

let article = Article(comment: comment, createdAt: NSDate())
article.save()

Articleに値をセットし、save()を呼ぶだけです。

JSONの返却

コードを見たまんまですが、

response.status(HttpStatusCode.OK).sendJson(json)

これで、HTTPステータスコードとJSONを返却できます。

サンプルの動かし方

リポジトリのREADMEに書いておいたので、そちらを見てくださいw
https://github.com/d-abe/kitura_sample/blob/master/README.md

Macなら、Swiftと依存ライブラリが入れてあれば、makeして.build/debug/KituraSampleを実行するだけでいけると思います。

おわりに

実際には入力のバリデーションだったり、結果のPaginationだったりと色々やらないといけないことはたくさんあります。

その辺りに関しても便利なライブラリがあれば紹介していきたいです。

また、fluentはSQLインジェクションの脆弱性がありそう(SQL構築の際に文字列をそのまま結合している)なので、プレースホルダを作って扱うように修正した方が良さそうです。
これについては、Pull Requestがすでに上がっていたので、そのうちマージされるかもしれません。

LINEで送る
Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です