Edited at

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

前回=> vte.cxによるバックエンドを不要にする開発(6.採番)

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


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

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

vtecx_architecture.png

サーバサイドJSはサーバサイドで実行されますが、これはBFF(Backends for Frontends)といって概念的にはフロントエンドの範疇です。

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


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

では実際にサーバサイドJSを実行するサンプルプログラムを作成してみましょう。

aとbの2つのパラメータを受け取り掛け算をして結果を返すサービス(※)です。

serverフォルダ配下に以下のようにmultiply.tsxを作成してみましょう。

(※ 私達はサーバサイドJSを便宜上サービスと呼んでいます)


/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() を使用します。

これを使って ab を取得して、これを数値に変換した上でかけ算に使用し、result としてクライアントへ返します。クライアントに返すには、vtecxapi.doResponse() を用います。

vtecxapiの関数については、詳しくはドキュメントを参照してください。

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

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を実行してください。)

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

スクリーンショット 2019-09-18 13.07.46.png

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

npx webpack --env.entry=/server/multiply.tsx --mode=production --env.externals=true

Reactを必要とするもの(SSRやPDF出力など)では、env.externals=env.externals=falseとしてください。


クライアントからサービスを呼び出す

今度は、クライアントの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が表示されたら成功です。

スクリーンショット 2019-09-18 13.07.46.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)


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

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

ブラウザで、http://{サービス名}.vte.cx/s/getfeed?xを開いてください。

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

スクリーンショット 2019-09-19 9.41.37.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)


デプロイ

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.js

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スタイルシートについては、ドキュメントを参照してください。


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;'
}


デプロイ

npm run watch:server -- --env.entry=/server/ssr.pdf.js

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

スクリーンショット 2019-09-19 10.03.45.png

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