アプリエンジニアからサーバサイドエンジニアへの JSON オブジェクトに関するお願い

[Classi Advent Calendar 2016](http://qiita.com/advent-calendar/2016/classi) 3 日目です！

API レスポンスの JSON オブジェクトが統一されいないとクライアントサイドでのハンドリングが煩雑になるため、サーバサイド API を作る時に JSON のキー名は適切につけて欲しい、という内容です。

ここでは、クライアントサイドは Swift で書いていきます。

## JSON マッピング

例えば `users/1` という API が以下のレスポンスを返すとします。

```json
{
  "user": {
    "id": 1,
    "name": "渡辺曜",
    "age": 16
  }
}
```

このレスポンスをハンドリングするために、 JSON マッピングをしていくことになります。
ここでは [Himotoki](https://github.com/ikesyo/Himotoki) を使っています。


```swift
struct User: Decodable {
    let id: Int
    let name: String
    let age: Int

    static func decode(_ e: Extractor) throws -> User {
        return try User(
            id: e <| "id",
            name: e <| "name",
            age: e <| "age"
        )
    }
}
```

これで、JSON を Swift で扱えるようになりました。
`user.id`, `user.name` などでアクセスできるようになり、型も担保されいい感じです。 :blush: 

## 別の API の user オブジェクト

`users/1/best_friend` という API のレスポンスは以下のようになっていました。


```json
{
  "user": {
    "id": 2,
    "first_name": "千歌",
    "last_name": "高海"
  }
}
```

なんということでしょう、 user というキーは同じ名前なのに first_name と last_name という新しいものがあり、 age と name は消えてしまいました。 :scream:
このレスポンスをマッピングするとこうなります。

```swift
struct User: Decodable {
    let id: Int
    let firstName: String
    let lastName: String

    static func decode(_ e: Extractor) throws -> User {
        return try User(
            id: e <| "id",
            firstName: e <| "first_name",
            lastName: e <| "last_name"
        )
    }
}
```

しかし困ったことに `User` struct が 2 つ生まれてしまい、名前が衝突してしまいました... :cry: 

## クライアント側で解決するには？

struct 名を `UserOfFriend` など変えるか、 optional を使うしかありません。 

### struct 例: 

```
struct UserOfFriend: Decodable { ...
```

名前は衝突しなくなりました。
しかし、 JSON のキー名とオブジェクトの属性名が違ってしまいます。 :angry: 


### optional 例:

```
struct User: Decodable {
    let id: Int
    let firstName: String?
    let lastName: String?
    let name: String?
    let age: Int?

    static func decode(_ e: Extractor) throws -> User {
        return try User(
            id: e <| "id",
            firstName: e <| "first_name",
            lastName: e <| "last_name",
            name: e <| "name",
            age: e <| "age"
        )
    }
}
```

こうすれば `users/1` でも `users/1/best_friend` でも同じ `User` として利用できます。
ですが、これだと毎回 `nil` かどうかの確認をしなければならず、よくありません。
面倒くさくて `nil` チェックを怠る野蛮な人間が今後出てくる可能性もあります。 :cop:

## どうしてほしいか

クライアントで頑張には限界があるため、オブジェクトの中身が違うなら名前を変えてレスポンスを返して欲しいです。
`user` と `friend` のように JSON キー名を変えてもらうだけで、 `User` と `Friend` と別の型を用意することができました！ :tada:

|api  | json | mapping|
|---|---|---|
|users/1   |{<br>&nbsp;&nbsp;"user": {<br>&nbsp;&nbsp;&nbsp;&nbsp;"id": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"name": "渡辺曜",<br>&nbsp;&nbsp;&nbsp;&nbsp;"age": 16<br>&nbsp;&nbsp;}<br>}<br> | struct User: Decodable {<br>&nbsp;&nbsp;&nbsp;&nbsp;let id: Int<br>&nbsp;&nbsp;&nbsp;&nbsp;let name: String<br>&nbsp;&nbsp;&nbsp;&nbsp;let age: Int<br><br>&nbsp;&nbsp;&nbsp;&nbsp;static func decode(_ e: Extractor) throws -> User {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return try User(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id: e &lt;&#124; "id",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: e &lt;&#124; "name",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;age: e &lt;&#124; "age"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}|
|users/1/best_friend  | {<br>&nbsp;&nbsp;"friend": {<br>&nbsp;&nbsp;&nbsp;&nbsp;"user_id": 2,<br>&nbsp;&nbsp;&nbsp;&nbsp;"first_name": "千歌",<br>&nbsp;&nbsp;&nbsp;&nbsp;"last_name": "高海"<br>&nbsp;&nbsp;}<br>} |struct Friend: Decodable {<br>&nbsp;&nbsp;&nbsp;&nbsp;let id: Int<br>&nbsp;&nbsp;&nbsp;&nbsp;let firstName: String<br>&nbsp;&nbsp;&nbsp;&nbsp;let lastName: String<br><br>&nbsp;&nbsp;&nbsp;&nbsp;static func decode(_ e: Extractor) throws -> Friend {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return try Friend(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id: e &lt;&#124; "id",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastName: e &lt;&#124; "last_name",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstName: e &lt;&#124; "first_name"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}  |


## 最後に

JSON オブジェクトの命名を適切にしていただくだけで、クライアント側の負荷が減ります :smiley: 

私も API を作っていると同じ名前で別のオブジェクトを生成してしまうことがあるので、そうはならないように気をつけたり、 JSON を生成する処理をキーごとに共通化しています。
Rails でいうと、 `jbuilder` の `partial` を細かく分割するなどで対応出来ると思います。
(ただし `jbuilder` の `partial` は重いのであまり使うなという話も...)

配列なら `users` のように複数形にしてほしい、必須パラメータなら URLParameter でなく path に含めてほしいなどいろいろあります。
サーバサイドでやってほしいこと、クライアントサイドでやるべきことの認識を合わせ、互いに定時退社できるよう協力していきましょう！！