vte.cxによるバックエンドを不要にする開発(7.サーバサイドJavaScript)

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

今回はサーバサイドJavaScriptについて説明します。

## vte.cxのアーキテクチャーとサーバサイドJavaScript

これまでのサンプルは、エンドポイント``/d``に対してアクセスするもので、データの登録や参照だけを行うものでした。実は、vte.cxにはデータ登録/参照以外に、サーバサイドのJavaScript(以下、サーバサイドJS)を実行する機能があります。（下図のエンドポイント``/s``がこれに該当します）

<img width="869" alt="vtecx_architecture.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/ca7fbb3a-ec67-173b-ac6b-fed60cc196c6.png">

サーバサイドJSはサーバサイドで実行されますが、これはBFF(Backends for Frontends)といって概念的にはフロントエンドの範疇です。
つまり、実行される場所が異なるだけで、クライアントサイドで実行されるJavaScriptの機能と同等であるというわけです。ただ、VtecxAPIを使ってサーバリソースに直接アクセスしたり、PDF生成、メール送信といったバックエンド機能を利用できるというメリットがあります。また、``/d``が返すリソースで不必要な項目が多い場合に通信量削減のためクライアントが必要とする最低限のものだけサーバサイドで編集して返したりすることはよくあります。集計などもサーバサイドJSを利用するとよいでしょう。

## 掛け算を行うサンプルプログラム

では実際にサーバサイドJSを実行するサンプルプログラムを作成してみましょう。
aとbの２つのパラメータを受け取り掛け算をして結果を返すサーバサイドのJavaScriptコードです。
serverフォルダ配下に以下のようにmultiply.tsxを作成してみましょう。


```/server/multiply.tsx
import * as vtecxapi from 'vtecxapi'

const a = Number(vtecxapi.getQueryString('a'))
const b = Number(vtecxapi.getQueryString('b'))

const result = a * b
const res = {
	feed: {
		title: result.toString()
	}
}

vtecxapi.doResponse(res, 200)
```

クエリパラメーターを取得するためには `vtecxapi.getQueryString()` を使用します。
これを使って `a` と `b` を取得して、これを数値に変換した上でかけ算に使用し、`result` としてクライアントへ返します。クライアントに返すには、`vtecxapi.doResponse()` を用います。

vtecxapiの関数については、詳しくは[ドキュメント](https://vte.cx/documentation.html#index12)を参照してください。

次に、以下のコマンドでサーバにデプロイしてください。

```
npm run watch:server -- --env entry=/server/multiply.tsx
```
以下のように実行されればデプロイ成功です。
（もし、webpack is watching the files…で止まったままなら、multiply.tsxファイルを一部更新してみてください）

```
webpack is watching the files…

Hash: 753d666827b92d455048
Version: webpack 4.39.1
Time: 9812ms
Built at: 2019-09-18 13:00:49
               Asset      Size  Chunks             Chunk Names
./server/multiply.js  17.8 KiB       0  [emitted]  main
Entrypoint main = ./server/multiply.js
[0] ./src/server/multiply.tsx 322 bytes {0} [built]
    + 1 hidden module
dist/server/multiply.js --> http://{サービス名}.vte.cx/server/multiply.js
{"feed" : {"title" : "Put content."}}
```

このように、`/src/server/` の配下に `multiply.tsx` というファイル作成してデプロイするだけで、`/s/multiply` というエンドポイントが自動的に作成されます。

実際にブラウザから実行してみます。
``http://{サービス名}.vte.cx/s/multiply?a=3&b=2&x``と入れて開いてみてください。最後のxパラメータはXMLで表示させるという意味です.
(※ vte.cxは基本的にユーザーがログインして使用するタイプの業務アプリケーションを想定しているため、ログインしていないユーザーからサービスへのリクエストは許可しません。今回作成するサービスへのリクエストも、ログインしているユーザーからのものだけを受け付けます。まずはじめに``http://{サービス名}.vte.cx/login.html``からログインしてください。login.htmlがサーバに存在しない場合は、/deploy.shを実行してください。）

以下のように表示されたら成功です。

<img width="219" alt="スクリーンショット 2019-09-18 13.07.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/012d605f-72bd-8036-1506-2848ff1704e0.png">

ちなみに、サーバサイドJSのデプロイコマンドを``deploy.sh``に追加する際は、以下のように記述してください。

```shell
npx webpack --env entry=/server/multiply.tsx --mode=production --env externals=true
```
Reactを必要とするもの(SSRやPDF出力など）では、env externals=``env externals=false``としてください。

## クライアントからサーバサイドのJavaScriptコードを呼び出す

今度は、クライアントのaxiosからリクエストして、エンドポイント``/s/multiply``にクエリパラメーターとして二つの数字を渡すと、その二つをかけ算して返す、というシンプルなアプリケーションを実装してみます。

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

const App = () => {
  const [result, setResult] = useState(0)

  const multiply = async () => {
    try {
      axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
      const res = await axios.get('/s/multiply',{params: { a: 2, b: 3 }})
      setResult(res.data.feed.title)
    } catch (e) {
      alert('error:'+e)
    }
  }

  return (
    <div>
      <button onClick={() => { multiply() }}>
       {result}
      </button>
    </div>
  )
}

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

``npm run serve:login``でログインした後、``npm run serve:index``でデプロイしてください。

ボタンをクリックして以下のように６が表示されたら成功です。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/58eafa28-4777-a632-2cf7-3919b4feb417.png)

## vtecxapiを使ってデータを取得する

今度はvtecxapiを使ってデータベースからデータを取得してみましょう。
以下のように、``vtecxapi.getFeed()``を使うことで、これまで``/d/hotel``に対して行ってきたGETリクエストをサーバサイドJSから行うことができます。簡単ですね。

```/server/getfeed.tsx
import * as vtecxapi from 'vtecxapi'

const feed = vtecxapi.getFeed('/hotel')
vtecxapi.doResponse(feed)
```

これをデプロイしましょう。

```shell
npm run watch:server -- --env entry=/server/getfeed.tsx
```

ブラウザで、``http://{サービス名}.vte.cx/s/getfeed?x``を開いてください。
以下のようにfeedが表示されたら成功です。

<img width="455" alt="スクリーンショット 2019-09-19 9.41.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/9c303de9-396e-7a6f-fc39-71d3a8fcc5c9.png">

## ReactによるSSR(サーバサイドレンダリング)

Reactの機能を使うことでSSR(サーバサイドレンダリング)を実行できます。
デプロイして/s/ssr.htmlにアクセスすると、「Hello, vte.cx!」が表示されます。

```/server/ssr.html.tsx
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'

const element = (
        <h3> Hello, vte.cx! </h3>
)

const html = ReactDOMServer.renderToStaticMarkup(element)

vtecxapi.doResponseHtml(html)
```

デプロイ

```shell
npm run watch:server -- --env.entry=/server/ssr.html.tsx
```

## PDF出力

ReactのSSR機能を利用してPDFを出力することができます。
vte.cxのPDF出力機能のテンプレートにSSRで生成したHTMLを指定すると動的にPDFを生成します。以下のサンプルでは、/s/ssr.pdfにアクセスするとPDFを動的に生成しダウンロードします。

```ssr.pdf.tsx
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import * as pdfstyles from '../pdf/pdfstyles'

interface User {
  firstName: string
  lastName: string
}

function formatName(user: User) {
  return user.firstName + ' ' + user.lastName
}

const user: User = {
  firstName: 'Hello',
  lastName: 'vte.cx'
}

const element = (
  <html>
    <body>
      <div className="_page" style={pdfstyles._page}>
        <table style={pdfstyles._table}>
          <tr>
            <td>
              <p> Hello, {formatName(user)}! </p>
            </td>
          </tr>
          <tr>
            <td>
              <div className="_barcodeEAN" style={pdfstyles._barcode} />
            </td>
          </tr>
        </table>
      </div>
    </body>
  </html>
)

const html = ReactDOMServer.renderToStaticMarkup(element)

// PDF出力
vtecxapi.toPdf(1, html, 'hello.pdf')
```

また、PDF用のスタイルシート``/pdf/pdfstyles.ts``を置いてください。
PDFスタイルシートについては、[ドキュメント](https://vte.cx/documentation.html#index13)を参照してください。

```pdfstyles.ts
export const _page: any = {
  pagesize: 'A4',
  orientation: 'portrait'
}

export const _table: any = {
  bgcolor: '#FFEC8B',
  frame: 'box',
  cellspacing: '3',
  cellpadding: '3',
  width: '90%',
  align: 'left'
}

export const _barcode: any = {
  value: '4900000000000;'
}
```

デプロイ

```shell
npm run watch:server -- --env entry=/server/ssr.pdf.tsx
```

ブラウザから``http://{サービス名}/s/ssr.pdf``を開くと``hello.pdf``がダウンロードされます。

<img width="831" alt="スクリーンショット 2019-09-19 10.03.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17472/3f52eb0d-16d8-a2e1-2de7-f10a2293399c.png">


本日はここまでです。お疲れさまでした。

次回=>[vte.cxによるバックエンドを不要にする開発(8.gitフローと自動デプロイ)](https://qiita.com/stakezaki/items/ec35f17c6341be70003a)