🐢

Node.js(Express)でサクッとWebアプリ開発をする際のポイント

2022/07/20に公開

サーバーサイドJavaScript実行環境であるNode.jsについて学習したので基礎的な内容を紹介していきます。

Node.jsには非同期処理により大量アクセスをさばける、npmを利用して簡単にサーバーを起動できる等のメリットがあります。

ExpressとはNode.jsで動作するWebアプリケーションフレームワークで、基本的なセキュリティ対策があらかじめ実装されている点や構成がシンプルな点が評価されています。

今回はこれらを使ってNode.jsでのローカルサーバーを起動する方法やExpressの導入、ログ出力のやり方、ルーティング設定、テンプレートエンジン、セキュリティ対策について解説します。

Node.jsとnpmは以下のバージョンを使用しています。

  • Node.js: 16.13.2
  • npm: 8.1.2

Node.jsでローカルサーバー起動

適当なフォルダを作成してnpmで初期化します。

mkdir nodejs-sample
cd nodejs-sample
npm init

初期化時の設定は基本的にはデフォルトでOKですが entry point は app.js に変更しました。作成された package.json の内容は以下の通りです。

{
  "name": "nodejs-sample",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

app.js を作成します。

var http = require("http");
var server = http.createServer(function (req, res) {
  res.writeHead(200, { "content-type": "text/plain" });
  res.end("Hello World");
});
server.listen(3000);

http は Node.js にもとから含まれているパッケージで require を使って利用できるようにします。listen(<port number>) と書くことでリクエスト待つ状態にしています。

app.js を実行します。

node app.js

ブラウザで localhsot:3000 にアクセスすれば「Hello World」が表示されるはずです。

これでサーバー起動できました。

Expressの導入

ローカルサーバー起動

適当なフォルダを作って初期化した後に npm で experss をインストールします。

mkdir express-sample
cd express-sample 
npm init
npm i express

Expressのバージョンは4.18.1がインストールされました。

app.js を作成します。express を使うと以下のように書くことができます。

var http = require("http");
var express = require("express");

var app = express();

app.get("/", function (req, res) {
  return res.send("Hello World");
});

var server = http.createServer(app);
server.listen(3000);

app.js を実行します。

node app.js

ブラウザで localhsot:3000 にアクセスすれば「Hello World」が表示されるはずです。

これでサーバー起動できました。

ルーティング設定

get メソッドにパスを指定することでルーティングを設定できます。

var http = require("http");
var express = require("express");

var app = express();

app.get("/", function (req, res) {
  return res.send("Hello World");
});

app.get("/hoge", function (req, res) {  ←ここから3行追加
  return res.send("Hoge");
});

var server = http.createServer(app);
server.listen(3000);

ブラウザで localhsot:3000 にアクセスすれば「Hello World」が表示され [localhost:3000/hoge](http://localhost:3000/hoge) にアクセスすると「Hoge」が表示されるはずです。

このようにしてページ毎のパスを設定できます。

404ページを作成する場合は app.js の下の方に以下を追加すればOKです。

app.use(function (req, res, next) {
   var err = new Error("Not Found");
   err.status = 404;
   return res.render("error", {
     status: err.status,
   });
 });

サーバーエラーのページは以下を追加すればOKです。

app.use(function (err, req, res, next) {
   if (err.code === "EBADCSRFTOKEN") {  ← CSRF Token エラー
     res.status(403);
   } else {
     res.status(err.status || 500);
   }

   return res.render("error", {
     message: err.message,
     status: err.status || 500,
   });
 });

ログ出力

console.log を使ってもログ出力は可能ですが morganというログ出力用パッケージを使ってみます。

npm i morgan

バージョンは1.10.0がインストールされました。app.js でこれを使います。

var http = require("http");
var express = require("express");
var morgan = require("morgan");  ←追記

var app = express();

app.use(morgan("tiny"));  ←追記

app.get("/", function (req, res) {
  return res.send("Hello World");
});

var server = http.createServer(app);
server.listen(3000);

tiny というのがログのフォーマットのことで他にも combined, common, dev, short などを指定できます。tiny にするとリクエストに対する最小限の内容になります。

サーバーを起動してブラウザから localhsot:3000 にアクセスする際にターミナルで以下のような内容のログが出力されます。

GET / 304 - - 4.745 ms

ファイルにログ内容を出力したい場合は winston をというパッケージが便利です。

npm i winston

ログ内容の設定ファイルとして lib/logger.js を作成。出力先のフォルダ log も事前に作成しておきます。

var winston = require("winston");

 function Logger() {
   return winston.createLogger({
     level: "warn",
     format: winston.format.json(),
     transports: [new winston.transports.File({ filename: "log/warning.log" })],
   });
 }

 module.exports = new Logger();

これを app.js で読み込んで warn メソッドを使えばログファイルに出力できます。

var logger = require("./lib/logger");

app.get("/", function (req, res) {
   logger.warn(req.session.user);  ←ユーザー情報を出力

テンプレートエンジンの利用

HTML, CSS を共通化するための仕組みとしてNode.jsでもテンプレートエンジンが用意されています。pugとEJSの書き方を見てみます。

pug は構文がシンプルなので人気です。EJS は PHP の構文に似ているので PHP からの移行に適しています。

どちらも事前にnpmでインストールします。

npm i pug
npm i ejs

pug の場合はテンプレートファイルの拡張子をpugにします。

サンプルとして templates/index.pug を作成します。

doctype html
html(lang="ja")
  head 
    meta(charset="utf-8")
  body 
    h1 #{title}

これを express 側で読み込むために views, view engine を設定し render メソッドで対象のテンプレートを指定します。

app.js は以下のようになります。

var http = require("http");
var express = require("express");
var morgan = require("morgan");
var path = require("path");  ←追記

var app = express();

app.use(morgan("tiny"));

app.set("views", path.join(__dirname, "templates"));  ←追記
app.set("view engine", "pug");  ←追記

app.get("/", function (req, res) {
  return res.render("index", { title: "Hello Pug" });  ←追記
});

var server = http.createServer(app);
server.listen(3000);

サーバーを再起動してブラウザからアクセスすると title で渡した「Hello Pug」が表示されるはずです。

ejs の場合はテンプレートファイルの拡張子をejsにします。

template/index.ejs を作成してみます。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <h1><%= title %></h1>
  </body>
</html>

express のテンプレートエンジンの設定をejsに変えます。

app.set("views", path.join(__dirname, "templates"));
app.set("view engine", "ejs");  ←変更

app.get("/", function (req, res) {
  return res.render("index", { title: "Hello EJS" });  ←変更
});

サーバーを再起動してアクセスするとtitleという変数で渡した「Hello EJS」が表示されます。

セキュリティ対策

ヘッダ設定

Helmet というミドルウェアを使ってヘッダの設定を行うことで安全に HTTP 通信が行うことができます。読み込むだけなので簡単です。

npm i helmet
var helmet = require("helmet");

var app = express();

app.use(helmet());

XSS対策

XSS(クロスサイトスクリプティング)対策については pug などのエンプレートエンジンを使えば勝手に対応してくれるので便利です。投稿フォーム等で script タグを入力してもエスケープされます。

CSRF対策

別サイトから勝手に投稿されたりしないように CSRF(クロスサイトリクエストフォージュリ)対策も必要です。express-session を使っていれば csurf というパッケージが利用できます。

npm i csrf

app.js のルーティング設定で get の際は csrf() を使ってトークンを生成して渡します。post の際にトークンのチェックを行うようにします。

var csrf = require("csurf");

var csrfProtection = csrf();

app.get("/update", csrfProtection, function (req, res, next) {
 return res.render("update", { csrf: req.csrfToken() });
});

app.post("/update", fileUpload(), csrfProtection, function (req, res, next) {
  〜
  〜
  〜
});

フォームでトークンを送ります。

input(type="hidden" name="_csrf" value=csrf)

こうすることで正規の投稿画面からフォーム送信が行われなかった場合に弾くことができるようになりました。

まとめ

Express はシンプルなWebアプリフレームワークなので必要最低限の機能だけでいい場合などは使い勝手が良さそうです。
必要なライブラリも npm で簡単に追加できるので JavaScript やってきた人には入りやすいと思います。
そして何より Node.js と npm があれば動作するという手軽さはいいですね。

参考

Node.js とは
express - npm
morgan -npm
pug - npm
ejs - npm
winston - npm
Node.js + Express入門 - JavaScriptとコマンドラインがちょっとわかる人がNode.jsを使うサーバーサイド開発に入門するための本
JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう 〜 その1 〜(改訂版三版)
JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう ~ その2(iOS対応版)

Discussion