ORMを使用してAPIを作る (Kitura/ PostgreSQL)

# はじめに
この記事ではIBM製のSwiftでサーバーサイドを実装することのできるフレームワークの**Kitura**を使用してRESTful APIを作成するときの大まかなポイントをまとめていきます。ハンズオンではないです。

今回は、**PostgreSQL**のORMを併用してデータを保存と取得ができるAPIを作る想定で進めていきます。

### 今回使用するライブラリのうち、自分で追加する必要のあるライブラリ

|ライブラリ名|バージョン|
|---|---|
|SwiftKueryORM|0.6.1|
|SwiftKueryPostgreSQL|2.1.1|

# Model
まずは、永続化するモデルの定義から。

SwiftKueryORMには`Model`というプロトコルがあり、それに準拠してモデルを作成します。ModelはCodableに準拠しています。

また、`var id: Int?`と記述しておくと、idがnilの場合に自動でidを振り与えてくれるらしいです。注意としては、手動でもidを設定することができるので**そのidがもう既に存在していないかは保存する前に確認した方が良い**ようです。

```swift
// id以外のプロパティは適当です。
struct Diary: Model, Equatable {
    var id: Int?
    var title: String?
    var content: String?
    var posterName: String?    
    var likedCount: Int?
    var createdAt: Date?
    var updatedAt: Date?
}
```

このあと、PostgreSQLをインストールしたり、Terminal上で

```bash
createdb データベース名
```

などを叩いて、初期の設定しなければならないのですが今回は省略します。

それでは、本題に入ります！

# GET
Kituraは`Router`というクラスを使用してルーティングを設定します。`App`クラスからプロパティとして取得できるのでそれを使用します。

```swift
app.router.get("/api/v1/diary/read") { (request, response, next) in   
}
```

getメソッドの第一引数はルーティングを指定します。この場合、ローカル環境なら `http://localhost:8080/api/v1/diary/read`にアクセスすればコールバックが呼ばれます。

もしクエリパラメータ `?id=1234` みたいなものを取得したい場合は、`request.queryParameters`で取得できます。

```swift
let id = request.queryParameters["id"]
```

## READ (PostgreSQL)
GETリクエストを受け取ったら全件取得をします。
ORMを使用して非常に簡潔にかけます。
取得したいエンティティクラスから**`.findAll`**メソッドを呼ぶだけです。例えば、上のDiaryモデルとして保存したデータを取得したい場合は

```swift
Diary.findAll { (diaries: [Diary]?, error: RequestError?) in
}
```

このような感じで取得することができます。

まとめると、

```swift
app.router.get("/api/v1/diary/read") { (request, response, next) in   
    Diary.findAll { (diaries: [Diary]?, error: RequestError?) in
        guard let diaries = diaries else { return next() }
        response.send(diaries)
        next()  
    }
}
```
これで、データを取得してレスポンスで返せると思います。

# POST
postは初見でやったときは結構はまりました。。。まとめにも記載しますが、[Codable Routing](https://developer.ibm.com/swift/2017/10/30/codable-routing/)を使用しないで`request.body?.asRaw`でData型を取得するにはミドルウェアを指定する必要があるようです。

```swift
app.router.post("/api/v1/diary/create", middleware: BodyParser())
```

このBodyParser()によって、送られてきたBody部分を適切なプロパティに流してくれるのだと僕は考えています。
> Note: If you have not declared a Content-Type header, ParsedBody will be nil.
> 引用: https://ibm-swift.github.io/Kitura/Classes/BodyParser.html

ただ、リクエスト時に**`Content-Type`を指定しないとBody部分はnilになってしまう**ので注意です。

また、今回はCodableなので、`asRaw`プロパティにアクセスします。

```swift
app.router.post("/api/v1/diary/create") { (request, response, next) in
    guard let data = request.body?.asRaw else {
        return next()
    }
    guard let diary = try? JSONDecoder().decode(Diary.self, from: data) else {
        return next()
    }    
}
```

上のような流れで、送られてきたBody部分をDiaryモデルにデコードしています。

## CREATE (PostgreSQL)
KueryORMが`save`メソッドを用意してくれているのでそれを使いましょう。

```swift
// 本当は、保存する前に、idがユニークなものかを確認した方が良いです。。。
diary.save() { (diary: Diary?, error: RequestError?) in
    if let error = error {
        print(error) 
        return next()
    }
    guard let diary = diary else {
        return next()
    }
    response.send(diary)
}
```

```POSTリクエストの一連の流れ.swift
app.router.post("/api/v1/diary/create", middleware: BodyParser())
app.router.post("/api/v1/diary/create") { (request, response, next) in
    guard let data = request.body?.asRaw else {
        return next()
    }
    guard let diary = try? JSONDecoder().decode(Diary.self, from: data) else {
        return next()
    }    
    diary.save() { (diary: Diary?, error: RequestError?) in
        if let error = error {
            print(error) 
            return next()
        }
        guard let diary = diary else {
            return next()
        }
        response.send(diary)
    }
}
```

# まとめ

今回は、

- KituraでGETリクエスト・POSTリクエストをどのように行うのか。
- KituraでORMを使用したときにどのような流れでデータの取得・保存を行うのか。

がなんとなくでも掴めていただけたらめちゃくちゃ嬉しいです。
自分もまだ分かっていない部分が多いので、もっとKituraに強くなりたいと思います。

ちなみに、[Codable Routing](https://developer.ibm.com/swift/2017/10/30/codable-routing/)を使用して書くともう少しスマートに書けるので別記事で紹介したいです。