目次
はじめに
Node.jsについて学んでみました。
Vue.jsのAPIサーバーとしてExpressも選択肢の1つとしてあることを知った(気づいた)のがきっかけです。
※Node.jsについて深掘りした結果長くなったので、Expressは記事を分けました。
✅ゴール
Node.jsとExpressの理解を深める
✅環境
Windows
✅参考教材
📚Node.js超入門
以下、超入門。
テンプレートエンジンにEJS活用。fetchメソッド使っていたり割とモダン。
以前はejs使うのでそこで抵抗感が出たけど悪くなさそう。Kindleで購入したのがミス。
後半はXAMPPでサーバー立ててMySQLを活用したExpressでの掲示板アプリ制作をする。
以下、クックブック
Node.js
✅Node.jsとは
JavaScript言語のランタイム環境(プログラムを実行するための環境)兼ライブラリの管理を担う(開発プラットフォーム)。
Webサーバー(httpオブジェクト)まで作ることができるコマンドプログラム。(つまり、サーバープログラムそのものから作ることができる)
✅使ってみる
起動と動作確認。
node > console.log("hello"); hello undefined
サーバー起動
node > .editor // エディター起動 require('http').createServer((rq, rs)=>{rs.end('Hello Node.js');}).listen(8080); // ctrl + Dで実行
http://localhost:8080/へアクセス。

基本的なサーバー構築
方法をまとめると以下。
①インターネットアクセスをするhttpオブジェクトを読み込む
②httpからサーバー用オブジェクトを作成
③サーバーオブジェクトを待ち受け状態にする※外部アクセスが来たら対応するもの
hotnodeモジュールで自動更新
インストール
npm -g i hotnode
server.jsの作成
const http = require('http'); const server = http.createServer((request, response) => { response.writeHead(200, {'Content-Type': 'text/html'}); response.end('Hello Node.js'); }); server.listen(8080);
httpモジュール(オブジェクト)をロード。requireはNode.js固有のモジュールローディングシステム。
※httpモジュールでHTTPアクセスのための機能を提供する。
変数 = require( ID );
http.createServer()メソッドでhttp.Serverというサーバーのオブジェクトを作成。
引数に関数を用意し、以下のように定義する。
変数 = http.createServer( (request, response) => { ~~処理~~ });
※requestにはhttp.ClientRequest、responseにはhttp.ServerResponse(ヘッダー情報も扱える)というオブジェクトが引数に収められている。
.end()メソッドはクライアントへの返信を終了するメソッド。引数にテキストを用意しておくと出力して終了する。
※endメソッドですべてのテキストを書きだすのは大変なのでresponse.write()メソッドで短く区切って出力する方法もある。
http.Serverオブジェクトの.listen()メソッドで待ち受けるポート番号を指定している。
起動
hotnode server.js
http://localhost:8080/へアクセス

※別パターンでの確認
curl http://localhost:8080/ >> <h1>Title</h1>Hello Node.js
URLルーティングの設定
pathモジュールの活用。basenameメソッドを用いてrequestオブジェクトのに格納されたURLのbasenameを抽出。
const http = require('http'); const path = require('path'); const pages = [ {route: '',output: 'TOP'}, {route: 'about',output: 'about'}, ]; const server = http.createServer((request, response) => { let lookup = path.basename(decodeURI(request.url)); pages.forEach(function(page) { if(page.route === lookup ) { response.writeHead(200, {'Content-Type': 'text/html'}); response.end(typeof page.outpuut === "function" ? page.output():page.output); } }); if(!response.finished) { response.writeHead(404); response.end("not found!"); } }); server.listen(8080);
確認


ルーティングが設定できているのがわかりますね◎
複数階層でのルーティングの場合
path.basenameの代わりにrequest.urlを使う。
const http = require('http'); const pages = [ {route: '/',output: 'TOP'}, {route: '/about',output: 'about'}, {route: '/about/this',output: 'about/this'}, // add ]; const server = http.createServer((request, response) => { let lookup = decodeURI(request.url); // edit ~~割愛~~
確認

✅URLモジュールを利用した場合
※超入門参考
url.parse(request.url)でクライアントがアクセスしたURLを整理して取り出し。
switch部分でパース処理した値のpathnameを取り出し。/helloだとhelloの部分が取り出される。
const url = require('url'); ~~~~ function getFromClient(request, response){ var url_parts = url.parse(request.url, true); switch (url_parts.pathname) { case '/': response_index(request, response); break; case '/other': response_other(request, response); break; default: response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('no page...'); break; } }</pre> url.parseを用いてURLデータをパース処理(データ解析して本来の状態に組み立てなおす)、ドメインやパス部分など、URLを構成するそれぞれの要素に分けて整理、ルーティングを分岐。⇒CSSの適用にも使われる※超入門P109 ためしにパース処理されたURLをコンソールに出力してみました。 編集 <div> <pre> response.end('no page...'); break; } console.log(url_parts); }</pre> 確認 </div> <pre>Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: [Object: null prototype] {}, pathname: '/other', path: '/other', href: '/other' }</pre> ついでにconsole.log(request)を出力したところ、ターミナル内に表示しきれない程、膨大な情報が表示されました。まだまだ知らないことが多すぎる。 <strong>✅スタイルの適用</strong> index.ejsのヘッドタグ内に以下を追記。 <div> <pre></pre> stylesheet.cssの作成 <div> <pre>h1 { color: blue; }</pre> </div> </div> </div> app.jsに以下を追記 <div> <pre>const style_css = fs.readFileSync('./style.css', 'utf8'); ~~~~ case '/style.css': response.writeHead(200, {'Content-Type': 'text/css'}); response.write(style_css); response.end(); break;
確認

http://localhost:3000/style.cssへアクセス

HEAD情報を加える
①HTMLの<head>タグ内に用意する
②http.ServerResponseのメソッドを利用する
ヘッダー情報の設定
response.setHeader( 名前, 値 );
ヘッダー情報の取得
変数 = response.getHeader( 名前 );
ヘッダー情報の出力
response.writeHead( コード番号, メッセージ );
※ステータスコードを付けて出力する。
✅HTMLタグの適用
.endメソッドにそのまま記入すればOK
response.end('<h1>Title</h1>Hello Node.js');

.end()メソッドの1文で記述するのが困難な場合は、response.writeメソッドを活用する。
const http = require('http'); var server = http.createServer( (request,response)=>{ response.setHeader('Content-Type','text/html'); response.write('<!DOCTYPE html><html lang="ja">'); response.write('<head><meta charset="utf-8">'); response.write('<title>Hello</title></head>'); response.write('<body><h1>Hello Node.js</h1></body></html>'); response.end(); } ); server.listen(3000);

HTMLファイルの利用
参考:node.jsでファイルの入出力操作、クックブックP8~
fs(File System)モジュールを活用する。
一般的にファイルの入出力は時間がかかる処理のため、同期処理、非同期処理のメソッドが用意されている。
変数 = require('fs');
✅ファイルの読込
⭐非同期処理メソッドはコールバック関数で処理を明示しないとエラーになるので注意!!
readFileSync:同期処理メソッド
ファイルの読み込みが完了してから、後続の処理を行いたい場合に用いる。
sample.jsを作成
const hoge = require('fs').readFileSync('hogehoge.txt','utf-8'); console.log(hoge);
※別途hogehoge.txtを作成。
確認
node sample.js >> hogehoge
utf-8を指定していないとバイナリデータで→のように表示されました。<Buffer 68 6f 67 65 68 6f 67 65>
readFile:非同期処理メソッド
ファイルの読み込みを待たずに後続処理を捌きたい場合に用いる。第3引数の関数は読込終了後実行される。
fs.readFile( ファイル名, エンコーディング, 関数);
✅txtファイルの書き換え
const fs = require('fs'); let text = "書き出したい内容"; fs.writeFileSync("hoge.txt", text);
実行するとhoge.txtファイルの中身が書き換わる。

✅HTMLファイルの書き換え
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html"; charset="UTF-8"> <title>Index</title> </head> <body> <h1>dummy_title</h1> <p>dummy_content</p> </body> </html>
server.js
const http = require('http'); const fs = require('fs'); var server = http.createServer(getFromClient); server.listen(3000); console.log('Server start!'); function getFromClient(request,response){ fs.readFile('./index.html', 'UTF-8', (error, data)=>{ var content = data. replace(/dummy_title/g, 'title'). replace(/dummy_content/g, 'content'); response.writeHead(200, {'Content-Type': 'text/html'}); response.write(content); response.end(); }); };

replace();メソッドを活用。 参考
任意の文字列を別の文字列に置き換える(置換)ことができるメソッド。
以下のように、gフラグを付与することで複数の文字列を置き換えることが可能。
処理をしたい変数.replace(/置き換えたい文字列/g, '換わりになる文字列');
正規表現でも用いられるとのこと。ついでに見つけた正規表現の記事。いろいろ言われるsejukuだけど参考になる。
JSONデータの活用
✅基本的な使い方
参考:クックブック3章
JSON.stringifyを用いてJSON形式にフォーマットされた文字列を返す。
json-sample.jsの作成
module.exports = { shopA:{ name: 'メロン', price: 500, place: '東京' }, shopB:{ name: 'スイカ', price: 500, place: '宮城' } };
out.jsの作成
var data = require('./json-sample'); // profilesオブジェクト全体を文字列に変換 data = JSON.stringify(data); // 置換後の文字列をJSONに戻す data = JSON.parse(data); console.log(data); console.log(data.shopA.name);
実行
> node out.js { shopA: { name: 'メロン', price: 500, place: '東京' }, shopB: { name: 'スイカ', price: 500, place: '宮城' } } メロン
参考:Node.jsのexportsとmodule.exportsの違いについてのメモ
ためしにexportsに書き換えて実行してみる。
// module.exports = { exports.obj = {
実行
> node out.js { obj: { shopA: { name: 'メロン', price: 500, place: '東京' }, shopB: { name: 'スイカ', price: 500, place: '宮城' } } }
実際に書き換えて実行すると違いが分かりやすいですね。階層を考慮しなくて済むので、module.exportsの方が楽ですね。
ちなみに文字列でJSONに戻さないと以下になる。
var data = require('./json-sample'); // dataオブジェクト全体を文字列に変換 data = JSON.stringify(data); console.log(data);
実行
> node out.js {"shopA":{"name":"メロン","price":500,"place":"東京"},"shopB":{"name":"スイカ","price":500,"place":"宮城"}}
また、console.log(data.shopA)とやるとエラーになることも確認。
🙄やっとJSON変換&文字列変換の重要性に気付くことができました。
文字列だと正規表現を適用できて、JSONだとオブジェクトでデータを処理することができるイメージ。
✅JSのオブジェクトとJSONの違い
参考:
JavaScriptのオブジェクトとJSONは別物
JavaScriptのJSONを理解してデータを活用できるようにしよう
JSONはテキストデータ(=通信に最も適した形式)。テキストデータが最も軽量のため、オブジェクトをテキストデータに変換している。
⇒そのため、.つなぎでデータを参照したり、pushメソッドやdeleteメソッドを使うこともできない。
JavaScriptのオブジェクトはキーがクォーテーションなし、値の文字列はクォーテーションあり。
JSONデータはキーをダブルクォーテーションで囲み、文字列も囲む。
JSON.stringifyでJSのオブジェクトをJSON文字列に変換。
JSON.parseでJSON文字列をJSオブジェクトに変換。
✅.jsonファイルを扱う
参考:JSONフォーマットとは?具体的な書き方についても徹底解説!!
.jsonファイルを作成
{ "name": "メロン", "price": 500, "place": "東京" }
app.jsの作成
const http = require('http'); const fs = require('fs'); const url = require('url'); const json = fs.readFileSync('./sample.json', 'utf8'); var server = http.createServer(getFromClient); server.listen(3000); console.log('Server start!'); // createServerの処理 function getFromClient(request, response){ var url_parts = url.parse(request.url, true); switch (url_parts.pathname) { case '/': response.writeHead(200, {'Content-Type': 'text/html'}); response.write('<head><meta charset="utf-8"></head>'); response.write(json); response.end(); break; default: response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('no page...'); break; } }
確認

🙄自前のJSONデータをサーバーに上げることができた◎headタグにメタ情報を入れないと文字化けする点が気になる。。。
CSVファイルを扱う
データ自体にカンマが入っていたりなどの問題点を解消するため、ya-csvモジュールをインストール。
npm i ya-csv
✅CSVファイルの生成
csv.jsの作成
var csv = require('ya-csv'); var writer = csv.createCsvFileWriter('data.csv'); var data = [['a','b','c','d','e','f','g'],['h','i','j','k','l','m','n']]; data.forEach(function(rec){ writer.writeRecord(rec); });
createCsvFileWriter()メソッドでya-csvのCsvWriterインスタンスの生成。ファイルに書き込むためのwriteStreamオブジェクトが格納されている。
writeRecordメソッドはそれぞれの配列をデータ行に再構築(一次元配列をCSVデータの1行として処理。)して、fs.WriteStreamインスタンスに渡す。
実行
node csv.js
data.csv生成
"a","b","c","d","e","f","g" "h","i","j","k","l","m","n"
✅CSVファイルの読込
read-csv.jsを作成
var csv = require('ya-csv'); var reader = csv.createCsvFileReader('data.csv'); var data = []; reader.on('data', function(rec){ data.push(rec); }).on('end', function(){ console.log(data) });
実行
> node read-csv.js [ [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ], [ 'h', 'i', 'j', 'k', 'l', 'm', 'n' ] ]
クエリパラメーターの活用
※参考:超入門P124~
クエリーパラメーターとは、URLの末尾に付ける値のこと。
取り出し方
①request.urlの値をパース処理。第2引数のtrueでクエリ-パラメーターもパース処理される。
var url_parts = url.parse(request.url, true);
②queryプロパティにパース処理されたクエリーパラメーターのオブジェクトが保管されているので代入。
var query = url_parts.query;
/?msg=helloだと、{‘msg’:’hello’}というオブジェクトになる。
※parseメソッドでtrue(もしくはfalse)を付けておかないと’meg=hello’とただのテキストになる。
あとはquery.msgで取り出せばOK。
フォーム送信(POST)を扱う
※超入門参考(クックブックでは2章)
PHPでは$_POST[]を使い、プログラムの処理を行う時点でPOSTデータの値に直接アクセス可能。
NodeではPOSTデータを含んだHTTPメッセージそのものを扱うことができるため、どのように扱うかは開発者判断。
流れ①データ取得②データをパース③必要な値を取り出してパース
GETはいつ、どこからどうアクセスされても常に同じ結果が返されるもの。POSTはその時、その状況で表示を行うものに使われる。
Query Stringモジュール(クエリーテキストを処理するための機能を提供)を使う。
requestのmethodプロパティでリクエストの方式(GET/POSTなど)を判断。
if ( request.method === 'POST' ){~~~
イベント処理。on()メソッドの中でイベントに応じて呼び出される関数を設定できる。
オブジェクト.on(イベント名, 関数);
dataイベント(クライアントからデータを受け取ると発生するイベント)で情報を取得。関数の引数にクライアントから受け取ったデータが入る。
endイベント(データの受け取りが完了すると発生するイベント)で、dataイベントが小分けに送られてくる場合を配慮。全てのデータを受け取ったら、データをパース処理(クエリーテキストからエンコードしないといけない)。
// データ受信のイベント処理 request.on('data', (data) => { body +=data; }); // データ受信終了のイベント処理 request.on('end',() => { var post_data = qs.parse(body); // データのパース msg += 'POST送信で値「' + post_data.msg + '」を受け取り。'; var content = ejs.render(other_page, { title:"Other", content:msg, }); response.writeHead(200, {'Content-Type': 'text/html'}); response.write(content); response.end(); });
✅諸々の変数をコンソール出力
・dataイベントで渡されるデータ
request.on('data', (d) => { body +=d; console.log(d); });
適当にフォームに値を入力して送信したところこうなる。
<Buffer 6d 73 67 3d 25 45 33 25 38 31 25 38 32 25 45 33 25 38 31 25 38 32>
・dataイベントの値パース処理後
var post_data = qs.parse(body); // ★データのパース msg += 'POST送信で値「' + post_data.msg + '」を受け取り。'; console.log(post_data); var content = ejs.render(other_page, {
確認
[Object: null prototype] { msg: 'ああ' }
index.ejsの関連部分抜粋
<form method="post" action="/other"> <p> <input type="text" name="msg">
index.ejsで記載したinputのname属性と連携しているのがわかりますね。
今後深掘りしたい知識
・Websocketで双方向コミュニケーション
・1000番未満のポート番号はrootアクセス権限が必要らしい
・Expressでセッション機能(ミドルウェア)の追加
躓いた点
✅文字化け

ヘッドタグ内で文字エンコーディング指定されるよう追記して解決。
これにより、クライアント側にUTF-8の日本語テキストでデータが送信されることを伝える。
res.write('<head><meta charset="utf-8"><title>Hello</title></head>');

最初はresponse.write(‘<html lang=”ja”>’);で解決すると思いきや違った。
✅hotnodeでエラー
たとえば以下のようなスクリプト。
至って普通科と思いきや。。。
~~~ server.listen(3000); console.log('Server start!');
実行するとエラー。
hotnode node process restarted C:\Users\ユーザー\AppData\Roaming\npm\node_modules\hotnode\javascript\hotloader.js:112 return util.print(data.toString());
nodeでは実行される。
> node server.js Server start!
原因はわからないが、とりあえずnodeコマンドで試していこうと思う。
おわりに
Expressを用いても、MySQLでDB層を用意することに代わりはない。
であれば、Laravelを使うと思った。
また、サーバーレスであり、API層を考えずにDBを活用、さらには認証機能、デプロイまでできてしまうFirebaseはとてもいいシステムだなと改めて思った。
6/7追記:
TechpitのReact教材でAPIサーバーを叩くのに、プロジェクト下にfrontとserverディレクトリを設けていて、serverディレクトリで別途サーバーを起動しているのが印象的だった。
これの仕組みは至ってシンプルで、nodeでサーバーを起動。React側からnodeで指定したポート番号のlocalhostに通信しているだけだったことにようやく気付けた。
また、JSONデータの変換の意味を理解できたのは収穫。
次はAPIサーバー周りにチャレンジしたい。
※超入門もなんだかんだ勉強になる。
サルでも分かるExpressでのjsonAPIサーバーの作り方
API+JSONのサーバを作るにはExpress使うのが良さげ(ドキュメントがそれしかない)。
✅参考
Vue.js + ExpressでSSRをやってみた
Windows10 + Express & Pug(旧Jade)
.jadeはExpressにデフォルトで使われる拡張子。今はPugに名前が変わったらしい。(Expressのテンプレートは自由!!)
JavaScriptテンプレートエンジンのPug(Jade)とEJSの比較
JSにこんなテンプレートエンジンがあったなんて。
# CommonJS と ES6の import/export で迷うなら
CommonJSだとmodule.exports⇔ES6だとexport default
CommonJSだとrequire(‘module’)⇔ES6だとimport a from ‘module’
上記の感じで書き換えができる。
学習の備忘録
割愛した部分
超入門
P140 パーシャル、アプリケーション変数、クッキー
P262 jQueryでのAjax通信※JSON活用サンプル~RSS取得
P299 XAMPP&MySQL活用サンプル Queryメソッドを用いて実装
クックブック
P12~P30
メモ
・TCP(Transmission Control Protocol)はHTTP通信のバックボーンを提供。
・通常のWebサーバーのポート番号は80
・CommonJSとは、JavaScriptでサーバサイドやコマンドラインツール、GUIツールなど色んなアプリを開発するための標準的なAPIの仕様
コメントを残す