サーバサイドJavaScriptの実装例と単体テスト

先日、フロントエンドが主導してAPIのI/Fを決定していく開発の話をしました。
このあたりは実際のコードがないとイメージしずらいでしょう。

この記事は古いので、[こちら](https://qiita.com/stakezaki/items/d596afd085988d76983c)を参照してください。

-------------
今日はvte.cxにおけるサーバサイドのサービス実装方法についてサンプルを交えて説明したいと思います。サンプルコードは、[vtecxblank](https://github.com/reflexworks/vtecxblank)に置いています。
動かし方は、[React+vte.cxで業務アプリを作る　#1（環境設定〜動作確認まで）](http://qiita.com/stakezaki/items/0401d79d392b081fb85e)を参考にしてください。

#サーバサイドJavaScriptの基本

基本的なところについては、まず、[【vte.cx応用】20.サーバーサイドJavascriptについて](http://qiita.com/takeyama/items/2d06b6bc9e48f28b9f03)に目を通してください。
 ***注) 2017/7 webpack対応に伴い、module.exportsで括らなくてもいいようにしました。従来の方法で使いたい場合は、/_settings/propertiesのrightsに、use_webpack=falseを設定してください。*** 

この例にあるように、getfeed.jsを/serverフォルダに置いてデプロイし、/s/getfeedにアクセスするとサーバサイドで実行されて/registrationの配下の内容がFeedで返ります。簡単ですね。
また、reflexcontextのAPIについては、[ドキュメント](http://reflexworks.jp/documentation.html#taggingservice.html#J)を参照してください。

```server/getfeed.js
import reflexcontext from 'reflexcontext' 

const feed = reflexcontext.getFeed('/registration')
reflexcontext.doResponse(feed) 
```

#複数クラスのサンプル

ただ、上記の例は1つのファイルしかありません。
単純なものであればそれいいのですが大規模になってくると困ります。通常は複数のクラスにわけて実装すべきでしょう。

vte.cxでは、webpackにより様々なモジュールをバンドルすることが可能です。
例えば、以下のように、index.jsからPersonクラスをimportして、person.say()メソッドを呼ぶことができます。

```javascript:app/server/index.js
import reflexcontext from 'reflexcontext' 
import Person from './person'

const person = new Person('Steve')
reflexcontext.log(person.say())
reflexcontext.sendMessage(200, person.say())
```

```javascript:app/server/person.js
/* @flow */
export default class Person {
	name:string
	constructor(
		name:string = 'dummy'
	) {
    	this.name = name
	}

	say():string {
    	return 'Hello, I\'m ' + this.name + '!!'
	}
}
```

また、サーバログにも同じ内容が書き込まれています。
ブラウザから``https://{サービス名}.1.vte.cx/d/_log?x&f``を指定して確認してみましょう。ログの内容がXMLで表示されたかと思います。

#サーバサイドレンダリング

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

```javascript:ssr.html.js
import reflexcontext from 'reflexcontext' 
import React from 'react'
import ReactDOMServer from 'react-dom/server'

const element = (
        <h3> Hello, World! </h3> 
)

const html = ReactDOMServer.renderToStaticMarkup(element)

reflexcontext.doResponseHtml(html)
```

#SSRによるPDFの出力

さらに、SSRの機能を使ってPDFを出力できます。
[PDF出力機能](http://qiita.com/takeyama/items/bcb97ec4278bb39e89c5)のテンプレートにSSRで生成したHTMLを指定するとダイナミックにPDFを生成できるようになります。
以下のサンプルでは、/s/ssr.pdfにアクセスするとPDFを動的に生成しダウンロードします。

```ssr.pdf.js
import reflexcontext from 'reflexcontext' 
import React from 'react'
import ReactDOMServer from 'react-dom/server'

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

const user = {
	firstName: 'Harper',
	lastName: 'Perez'
}

const element = (
	<html>
		<body>
			<div className="_page" style={{ pagesize: 'A4', orientation: 'portrait'}}>
				<table>
					<tr>
						<td>
							<p> Hello, {formatName(user)}! </p> 
						</td>
					</tr>
				</table>
			</div>
		</body>
	</html>
)

const html = ReactDOMServer.renderToStaticMarkup(element)
 
// PDF出力
reflexcontext.toPdf({}, html, 'test.pdf')
```

#Mochaによる単体テスト

複数のクラスに分割できると単体テストもやりやすくなります。
以下にその方法について説明します。

まず、mochaとES6で書かれたコードをテストするespower-babelを入れます。

```bash
sudo npm install -g mocha
npm install --save-dev mocha
npm install --save-dev should
npm install --save-dev espower-babel
```
以下はserver/person.jsをテストするコードです。

```javascript:test/test_person.js
import should from 'should'
import Person from '../src/server/person'

let person

before(function(){
	person = new Person('Steve')
})

describe('Person', function(){
	it('person.say() is OK', function(){
		person.say().should.exactly('Hello, I\'m Steve!!')
	})
})
```

以下のように実行するとサービスの実行結果が表示されます。

```
$ mocha --compilers js:espower-babel/guess test/test_person.js

  Person
    ✓ person.say() is OK


  1 passing (12ms)
```

それでは、また。:relaxed: