vte.cxによるバックエンドを不要にする開発(2.データの登録と取得)

前回=>[vte.cxによるバックエンドを不要にする開発(1.Getting Started)](https://qiita.com/stakezaki/items/e526ca061d8f004db7f5)

### vte.cxのエンドポイント管理

今回はエンドポイントとデータの登録・参照について説明します。
vte.cxは任意のエンドポイントを作成でき、その配下にデータを格納できます。
エンドポイント名には英数文字列およびアンダースコア(_)を使用できます。

では実際に管理画面からエンドポイントを作成してみましょう。
エンドポイント管理タブの新規エンドポイント作成でfooというエンドポイントを作成してみてください。

<img width="1243" alt="スクリーンショット 2019-08-07 15.39.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/a28ec693-2c1d-baff-c8df-44387cd536bf.png">

うまく作成できれば以下のようにエンドポイント一覧に表示されます。

<img width="1188" alt="スクリーンショット 2019-08-07 15.36.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/9ebaab2d-bbe1-4db2-35cf-98dc18917291.png">

### 登録されるデータとentryについて

エンドポイントは一つのentryとしてサーバに登録されます。実際に以下のURLをブラウザで表示して確認してみましょう。/dに続けてエンドポイントの/fooの後に``?e&x``とパラメータを指定してください。(eはentry、xはxmlを意味します）

```
http://{サービス名}.vte.cx/d/foo?e&x
```

結果は以下のようにentryがXML形式で表示されたかと思います。(※ 実はxパラメータを取るとJSONで返しますが、セキュリティの都合上、ブラウザでJSONは表示されないようになっています。JSONはJavaScriptのXHR通信などで取得できます。）

<img width="398" alt="スクリーンショット 2019-08-07 15.54.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/b70b98cb-4613-eade-2780-96da2af6e021.png">

entry の中の``link href='/foo' rel='self' />``のhrefの値(/foo)をkeyといい、エンドポイントの値が入っているのがわかります。また、entry には一意の id が自動的に割り振られています。idは key(エンドポイント) と revision の組み合わせになっています。revision はentryの更新回数です。これが id に含まれているために、更新時に楽観的排他制御ができるようになっています。(楽観的排他制御については別途詳しく説明します）

`<id>key,revision</id>`

- key: /foo
- revision: 1

### エンドポイントの配下にデータを登録する

先程登録したエンドポイント/fooの配下にデータを登録することもできます。
VSCodeのプロジェクト直下にdataフォルダを作成して以下のXMLを作成してください。ファイル名は適当で構いませんがここでは``foo1.xml``としています。

```xml
<entry>
<title>Hello</title>
<link rel="self" href="/foo/1" />
</entry>
```

次に、``npm run upload:data``コマンドでサーバにデータを送信してください。
以下のように、``{"feed" : {"title" : "Accepted."}}``と表示されれば成功です。

```
$ npm run upload:data

> vtecxblank@1.0.0 upload:data /Users/foo/tutorial
> npx vtecxutil upload:data

data/foo1.xml --> http://{サービス名}.vte.cx/foo1.xml
{"feed" : {"title" : "Accepted."}}
```

本当にサーバが更新されているかブラウザで確認してみましょう。今度はeパラメータをf(feed)にして実行してみてください。

```
http://{サービス名}.vte.cx/d/foo?f&x
```

以下のように、登録したデータが表示されたかと思います。
このデータは、``/foo``エントリの配下にある``/foo/1``エントリです。
/foo配下には複数件登録できるため entry ではなく feed として表示されます。登録したデータは1件でしたが、feedの中には通常はentryが複数件存在する形になります。

<img width="423" alt="スクリーンショット 2019-08-07 16.40.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/81f4e6db-c868-34df-7561-7d9cbfdf057e.png">


ちなみに、JSON形式のデータも登録することができます。
以下は上記のXMLと同じ意味のJSONになります。
ただし、linkの属性である、hrefやrelの先頭には``___``(アンダースコア3つ)が付くので注意してください。また、全体を配列[]で括ってください。

```json
[{
	"title": "Hello",
	"link": [
		{
			"___href": "/foo/1",
			"___rel": "self"
		}
	]
}]
```

### データを取得して表示するプログラム

では、登録したデータを取得して表示するプログラムを作成してみます。
まずはじめに、ボタンをクリックしたら回数を表示する単純なReactのプログラムを作ってみましょう。サーバとの通信のところは後から付け加えたいと思います。
index.tsxを以下のプログラムに置き換えて表示させてみてください。

```index.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { useState } from 'react'

const App = () => {
  const [x, f] = useState(0)
  return (
    <div>
      <button onClick={() => { f(x+1) }}>
       {x} times
      </button>
    </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('container'))
```

前回学んだように、以下のコマンドでローカル環境とサーバ環境の同時に反映させて確認することができます。

```
npm run serve:index
```

ブラウザが起動して、``0 times``というボタンが表示されれば成功です。
ボタンを押して数字が増えていくのを確認してみましょう。

次に、実際にサーバのデータを取得するようにしてみます。
以下のようにコードを修正してください。
これは、axiosという通信ライブラリを使って、``/foo/1``のentryを取得して表示します。
実際には``/d/foo/1?e``をGETリクエストするわけですが、ブラウザ表示のときと違ってxパラメータはありませんので、entryをJSONで取得することになります。
HTTPヘッダに``X-Requested-With: XMLHttpRequest``をつけているのはセキュリティ(CSRF対策)のためですが、ここではおまじないだと思って必ずつけるということだけ覚えてください。

```index.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { useState,useEffect } from 'react'
import axios from 'axios'

const App = () => {
  const [x, f] = useState(0)

  const getdata = async () => {
    try {
      axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
      const res = await axios.get('/d/foo/1?e')
      alert(`res= ${res.data.title} `);
    } catch (e) {
      alert('error')
      console.log(e)
    }
  }

  useEffect(() => {
    getdata()
  })

  return (
    <div>
      <button onClick={() => { f(x+1) }}>
       {x} times
      </button>
    </div>
  )

}

ReactDOM.render(<App/>, document.getElementById('container'))
```

実行結果は``error``がアラート表示されたかと思います。
Chromeのデベロッパーツール(その他のツール>デベロッパーツール)のNetworkタブで確認すると、以下のように``403 Forbidden``が返ってきているのがわかります。

<img width="1230" alt="スクリーンショット 2019-08-08 10.53.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/ea2e4bb0-a8de-f29b-df5e-95ef3d54b460.png">

実は、``/foo/1``のエントリはログインしている状態でないと取得することはできません。
管理画面でエンドポイントを作成しても一般に公開されるわけではなく、デフォルトではログインしないとアクセスできないようにACL(Access Control List)が設定されています。（ACLについては別途詳しく説明します）

では、ログインするために、コンソールで起動中の``npm run serve:index``を一旦止め、代わりに``npm run serve:login``を実行してください。すると、ログイン画面が起動してきますので、サービスにログインしてください。ログインに成功して真っ白な画面が表示されたら、``npm run serve:login``を止め、もう一度、``npm run serve:index``を起動してみてください。

すると、以下の画面が表示されるかと思います。

<img width="1230" alt="スクリーンショット 2019-08-08 11.13.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/9895f986-8c91-015a-51b7-53e5b5f89232.png">

実際に``/foo/1``エントリのtitleが読まれ、``Hello``が表示されました。

今日はここまでです。お疲れ様でした。

次回の記事
[vte.cxによるバックエンドを不要にする開発(3.スキーマ定義と型の利用)](https://qiita.com/stakezaki/items/9aaf219fad60a7a4e3e2)