ReScriptでfetchを実行する -Twitter API 編-

ReScriptでfetchを実行します。
Twitter APIの一つにアクセスしてデータを取得します。

##試した環境
* macOS 10.13.6
* node v16.13.2

##事前準備
実際にサンプルを試したい場合は、Twitter API に登録して Bearer Token を取得しておきます。

##実行環境の作成
npmでインストールしていきます。[^rescript_installation][^Promise_installation][^node-fetch_installation]

```SH:SH
#ReScriptのインストール
git clone https://github.com/rescript-lang/rescript-project-template
cd rescript-project-template
npm install

#Promiseのインストール
npm install @ryyppy/rescript-promise --save

#node-fetchのインストール
npm install node-fetch@2 --save
```

`bsconfig.json`に以下を追加

```JSON:bsconfig.json
{
  "bs-dependencies": ["@ryyppy/rescript-promise"]
}
```

##ReScriptファイル
src ディレクトリに FetchExample.res を作成します。

```Rescript:src/FetchExample.res
module NodeFetchPolyfill = {
  type t
  @module external fetch: t = "node-fetch"
  @val external globalThis: 'a = "globalThis"
  globalThis["fetch"] = fetch
}

module Response = {
  type t<'data>
  @send external json: t<'data> => Promise.t<'data> = "json"
}

module User = {
  type t = { id: string, name: string, username: string}
  type headers = { 
    "headers": { "Authorization": string }
  }

  @val @scope("globalThis")
  external fetch: (string, 'params) => Promise.t<Response.t<{"data": Js.Nullable.t<array<t>>}>> = "fetch"

  let getUsers = (~token: string, ()) => {
    open Promise

    let params = {
      "headers": {
        "Authorization": `Bearer ${token}`
      }
    }

    fetch("https://api.twitter.com/2/users/by?usernames=TwitterDev,Twitter", params)
    ->then(res => {
      res->Response.json
    })
    ->then(data => {
      let ret = switch Js.Nullable.toOption(data["data"]) {
      | Some(data) => data
      | None => []
      }
      Ok(ret)->resolve
    })
    ->catch(e => {
      let msg = switch e {
      | JsError(err) =>
        switch Js.Exn.message(err) {
        | Some(msg) => msg
        | None => ""
        }
      | _ => "Unexpected error occurred"
      }
      Error(msg)->resolve
    })
  }
}

exception FailedRequest(string)

let _ = {
  open Promise
  let bearer = "TwitterのBearer Tokenを記載"
  User.getUsers(~token = bearer, ())
  ->Promise.then(result => {
    switch result {
    | Ok(products) =>
      Js.log("\nResponse:\n---")
      Belt.Array.forEach(products, p => {
        Js.log(`id: ${p.id} / name: ${p.name} / username: ${p.username}`)
      })
    | Error(msg) => Js.log("Could not query products: " ++ msg)
    }->resolve
  })
  ->catch(e => {
    switch e {
    | FailedRequest(msg) => Js.log("Operation failed! " ++ msg)
    | _ => Js.log("Unknown error")
    }
    resolve()
  })
}
```
https://github.com/ryyppy/rescript-promise/blob/master/examples/FetchExample.res を改変しました。
この先を実際に試す場合は、下の方にある`let bearer =`に事前準備で取得した Bearer Token を記入します。

##ReScriptをJavaScriptに変換
```SH:SH
npx rescript
```
rescript-project-templateディレクトリで実行します。
src にある FetchExample.res が FetchExample.bs.js に変換されます。

##実行
```SH:SH
node src/FetchExample.bs.js
```
rescript-project-templateディレクトリで実行します。
以下が表示されます。

```SH:SH
Response:
---
id: 2244994945 / name: Twitter Dev / username: TwitterDev
id: 783214 / name: Twitter / username: Twitter
```

##やっていること
`external`を用いて JavaScript の関数に型付をおこなっています。
https://rescript-lang.org/docs/manual/latest/external が参考になると思います。

##その他
`https://api.twitter.com/2/users/by?usernames=TwitterDev,Twitter`の`TwitterDev,Twitter`の部分を他のユーザ名に変更すると他のユーザの情報が取れます。カンマ区切りで100ユーザーまで一度に取得することができます。

[^rescript_installation]: https://rescript-lang.org/docs/manual/latest/installation を参照 
[^Promise_installation]: https://rescript-lang.org/docs/manual/latest/promise を参照
[^node-fetch_installation]: https://www.npmjs.com/package/node-fetch#loading-and-configuring-the-module を参照。ES Modulesだとうまく動きませんでした。CommonJSを使えるように v2 をインストールしています。