2025 年度 OSS リテラシ3 : node.js + express

参考資料

はじめに

Node.js による Web アプリケーション作成入門 11 節~ に,データベースとの連動の例が掲載されている. 今回はこれをベースに GET, POST されたデータをデータベースに保存するプログラムを作成する.

インストール

最新版の node.js をインストールする.Debian パッケージでは バージョンが古い (Debian 13 は v20.19) ので, node の最新の安定版 (v22.21) をインストールする.

$ sudo -s
# curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
# apt install -y nodejs

バージョンの確認

# nodejs --version
 v22.21.0

さらに確認用に curl コマンドをインストールしておく.

# apt install curl

# exit
$

アプリを Express で作成する (Node.js による Web アプリケーション作成入門 11 節の抜粋)

まず,アプリケーションを作成するフォルダを用意する.

$ mkdir iotex_app
$ cd iotex_app/

次に,npm 自体をアップデートした上で Express を導入する. mysql モジュールもあわせてインストールしておく.

$ sudo npm install -g npm
$ sudo npm install express-generator -g
$ sudo npm install mysql

アプリの作成

アプリを作成する

$ express --view=ejs myapp

   create : myapp/
   create : myapp/public/
   create : myapp/public/javascripts/
   create : myapp/public/images/
   create : myapp/public/stylesheets/
   create : myapp/public/stylesheets/style.css
   create : myapp/routes/
   create : myapp/routes/index.js
   create : myapp/routes/users.js
   create : myapp/views/
   create : myapp/views/error.ejs
   create : myapp/views/index.ejs
   create : myapp/app.js
   create : myapp/package.json
   create : myapp/bin/
   create : myapp/bin/www

   change directory:
        $ cd myapp

   install dependencies:
        $ npm install

   run the app:
        $ DEBUG=myapp:* npm start

myapp アプリのフォルダに移動して npm パッケージをインストールする.

$ cd myapp
$ npm install

サーバを起動する.

$ npm start

ブラウザなどで Express の初期画面が表示されることを確認する. XXX は自分の IP に変更すること.確認できたら Ctrl-C (コントロールキーと c を一度に打鍵する) で止める.

ブラウザ --> http://10.100.111.XXX:3000

加えて curl コマンドでも確認をする.

$ curl http://10.100.111.XXX:3000

  <!DOCTYPE html>
  <html>
    <head>
      <title>Get Method</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
       <h1>Get Method</h1>
       <p>Welcome to Get Method</p>
    </body>
  </html>

ファイルの編集

教科書で指示されているように,作成された app.js を変更する.

  • /users は使用しないため,/users 関連のコードを削除する.
  • var で変数宣言している箇所を const に置き換える.
  • 以下の 2 行を削除して app.use("/", require("./routes")); に置き換える.
    • const indexRouter = require('./routes/index');
    • app.use('/', indexRouter);

結果として,app.js は以下のようになる.

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// router
app.use('/', require('./routes'));

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

routes/index.js についても,var を const に変更しておく.

const express = require('express');
const router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

確認のために,再度サーバを起動する.

$ npm start

ブラウザなどで Express の画面が表示されることを確認する. XXX は自分の IP に変更すること.確認できたら Ctrl-C (コントロールキーと c を一度に打鍵する) で止める.

ブラウザ --> http://10.100.111.XXX:3000

加えて curl コマンドでも確認をする.

$ curl http://10.100.111.XXX:3000

  <!DOCTYPE html>
  <html>
    <head>
      <title>Express</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
       <h1>Express</h1>
       <p>Welcome to Express</p>
    </body>
  </html>

ブラウザや curl コマンドで表示される内容を決めているのは view/index.ejs である.view/index.ejs は以下のようになっており, <%= title %> に routes/index.js のres.render('index', { title: 'Express' }); で指定された title の中身 ('Express') が表示されていることが分かる.

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
</html>

GET, POST メソッドへの対応

routes/index.js を以下のように POST メソッドに関わる記述を追加する.

const express = require('express');
const router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'GET method' });   //タイトルを GET に
});

/* POST home page */
router.post('/', function(req, res, next) {
  res.render('index', { title: 'POST method' });   //タイトルを POST に
});

module.exports = router;

確認のために,再度サーバを起動する.

$ npm start

curl コマンドで GET, POST メソッドでアクセスする.それぞれ, タイトルが GET, POST になっていることを確認すること!!

以下は GET メソッドの場合の例である.タイトルが GET であることが分かる.

$ curl -X GET http://10.100.111.XXX:3000

  <!DOCTYPE html>
  <html>
    <head>
      <title>GET method</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
      <h1>GET method</h1>
      <p>Welcome to GET method</p>
    </body>
  </html>

以下は POST メソッドの場合の例である.タイトルが POST であることが分かる.

$ curl -X POST http://10.100.111.XXX:3000

  <!DOCTYPE html>
  <html>
    <head>
      <title>POST method</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
      <h1>POST method</h1>
      <p>Welcome to POST method</p>
    </body>
  </html>

送信データの表示.

入力データは GET の場合は req.query.<input> 要素の name 値で取得でる. POST の場合は req.body.<input> 要素の name 値で取得でる.

今回は GET or POST で,name (名前), time (時刻), temp (温度) を送信するので, それを個々の変数に入力し,表示できるようにする.

まず,ビューを作る. views/index.ejs で,name, time, temp, IP を表示できるようにする.

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
    <ul>
      <i>name: <%= name %> </i>
      <i>time: <%= time %> </i>
      <i>temp: <%= temp %> </i>
      <i>IP: <%= ip %> </i>
    </ul>
  </body>
</html>

次に app.js を編集して,変数設定 (const から始まる行) の下に 以下を追加する.POST で JSON 形式のデータを送信するためである.

app.use(express.json());  // JSONボディをパースするためのミドルウェア

最後に routes/index.js を修正する.

const express = require('express');
const router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
   const name = req.query.name;
   const time = req.query.time;
   const temp = req.query.temp;
   res.render('index', {
      title: 'GET method',
      name: name,
      time: time,
      temp: temp,
      ip:   ip
   });
});

/* POST home page */
router.post('/', function(req, res, next) {
   const name = req.body.name;
   const time = req.body.time;
   const temp = req.body.temp;
   res.render('index', {
      title: 'POST method (YAML)',
      name: name,
      time: time,
      temp: temp,
      ip:   ip
   });
});

module.exports = router;

GET メソッドでデータを送ってみる.

$ curl -X GET "http://10.100.111.XXX:3000?name=jxxx&time=20251030150000&temp=24.2"

   <!DOCTYPE html>
   <html>
     <head>
       <title>GET method</title>
       <link rel='stylesheet' href='/stylesheets/style.css' />
     </head>
     <body>
       <h1>GET method</h1>
       <p>Welcome to GET method</p>
       <ul>
         <i>name: jxxx </i>
         <i>time: 20251030150000 </i>
         <i>temp: 24.2 </i>
         <i>IP: ::ffff:10.0.0.103 </i>
       </ul>
     </body>
   </html>

POST メソッドで JSON 形式のデータを送ってみる.

$ curl -X POST http://10.100.111.100:3000   -H "Content-Type: application/json"   -d '{"name":"jxxx", "time":"20251030150000", "temp":24.2}'

  <!DOCTYPE html>
  <html>
    <head>
      <title>POST method (YAML)</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
      <h1>POST method (YAML)</h1>
      <p>Welcome to POST method (YAML)</p>
      <ul>
        <i>name: jxxx </i>
        <i>time: 20251030150000 </i>
        <i>temp: 24.2 </i>
        <i>IP: ::ffff:10.0.0.103 </i>
      </ul>
    </body>
  </html>

受け取ったデータをデータベースに保存する (Node.js による Web アプリケーション作成入門 12 節の抜粋)

routes/index.js を編集して以下を行う.

  • require 関数で mysql モジュールを読み込む
  • データベースの接続情報を定義する.
    • 接続先データベースは以前に作成した iotex とする.
  • データベースへの接続を行うコードの埋め込み
    • 教科書では GET メソッドの中で行っているが,1 回行えばよいので,外に出す方が良い.
  • 受け取ったデータをデータベースに入力する INSET 文を発行し,成功したらページを表示する.
    • 教科書ではプレースホルダを使っていないが,使った書き方にすべき.

これらを行った場合の routes/index.js の一部を抜粋する. POST メソッド部は自分で作成すること.また,穴埋め部分も行うこと.

const express = require('express');
const router = express.Router();
const mysql = require('mysql');  // mysql モジュールの呼び出し

/* MySQL 接続情報 */
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'iotex',
    password: 'xxxx',
    database: 'iotex'
});

connection.connect((err) => {
   if (err) {
      console.log('error connecting: ' + err.stack);
      return
   }
      console.log('success');
});

/* GET home page. */
router.get('/', function(req, res, next) {

   const name = req.query.name;
   const time = req.query.time;
   const temp = req.query.temp;

   connection.query(
        ....................... // SQL 文
        (error, results) => { 
           if (error) {
              console.error('DB Error:', error);
              return res.status(500).send('Database error');
           }
           // 成功したらページを表示
           .......
           .......
       }
   );
});

/* POST home page. */
.............
.............

課題:

前述の routes/index.js を完成させなさい.なお,プレースホルダを使う場合は,

connection.query(
     `INSERT INTO j4oss3 (name, temp) VALUES (?, ?)`,  //代入する箇所に ? を書く
      [name, temp],                                    //代入する変数の一覧を配列として与える.
      (error, results) => {
      // 以下略

のように書く.但し,3 行目の name, temp は変数である.

動作確認として,curl コマンドで GET, POST 両方の場合に対して URL を送信し,データベースに正しくデータが保存されたことを示す証拠を 提出しなさい.

具体的には,以下の 2 つを提出すること

  • curl コマンドの送信とその戻り値の「スクリーンショット」
  • select 文でデータベース中の該当テーブルの中身を表示した「スクリーンショット」
    • 但し,上記 curl コマンドで指定したデータが入力されている必要がある.

発展 (1) : パスワード保護

このプロジェクトに Basic 認証のようなパスワード保護をかけるには, 例えば routes/index.js の変数宣言 (const で始まる文) の後に, 以下のようなコードを追加すれば良い.以下の例では, ユーザ名 iotuser, パスワード iotpass になっているが, ユーザ名とパスワードは各自適切に設定すること!

function basicAuth(req, res, next) {
   const auth = req.headers['authorization'];

   if (!auth) {
         res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
         return res.status(401).send('Authentication required.');
   }

   const base64Credentials = auth.split(' ')[1];
   const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
   const [username, password] = credentials.split(':');

   // 認証情報のチェック(ここで任意のユーザー名とパスワードを設定)
   if (username === 'iotuser' && password === 'iotpass') {
        next(); // 認証成功
   } else {
        res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
        return res.status(401).send('Invalid credentials.');
   }
}

router.use(basicAuth);  // すべてのルートに適用

router.get('/', (req, res) => {
   // 認証済みの処理
   res.send('認証成功!');
});

確認方法

curl のオプションに -u iotuser:iotpass を付ける

$ curl -u iotuser:iotpass -X POST http://10.100.111.100:3000   -H "Content-Type: application/json"   -d '{"name":"jxxx2", "time":"20251030150012", "temp":24.2}'

発展 (2): HTTPS 化

HTTPS 化するために,セキュリティ対策 (HTTPS) で 設定した鍵ファイルを流用する.簡単のために,自分のホームディレクトリにコピーして利用する. 但し,ユーザ名は hogehoge ではなく自分のユーザ名に変更すること

$ cd ~/
$ sudo cp /etc/ssl/private/j4oss3.key ./
$ sudo cp /etc/ssl/certs/j4oss3.pem   ./
$ sudo chown hogehoge j4oss3.key            <-- 自分のユーザ名に
$ sudo chown hogehoge j4oss3.pem            <-- 自分のユーザ名に

express のアプリについては,HTTPS 化するためには app.js に以下の行を追加する. ポート番号は 3001 としている.

// HTTPS 用の鍵の設定 ----------------------
const fs = require('fs');
const https = require('https');

const options = {
   key: fs.readFileSync('/home/hogehoge/j4oss3.key'),   //<-- 置き場を修正!!
   cert: fs.readFileSync('/home/hogehoge/j4oss3.pem')   //<-- 置き場を修正!!
};

https.createServer(options, app).listen(3001, () => {
   console.log('HTTPS Server running on https://localhost:3001');
});
//-----------------------------------------

確認方法

データ送信先を https://iot-XX.matsue-ct.ac.jp:3001 のように, プロトコルを HTTPS にし, IP でなくホスト名で指定し,ポート番号を 3001 に変更する必要があることに注意.

$ curl -u iotuser:iotpass -X POST https://iot-XX.matsue-ct.ac.jp:3001   -H "Content-Type: application/json"   -d '{"name":"jxxx2", "time":"20251030160012", "temp":24.2}'

  <!DOCTYPE html>
  <html>
    <head>
      <title>POST success</title>
      <link rel='stylesheet' href='/stylesheets/style.css' />
    </head>
    <body>
      <h1>POST success</h1>
      <p>Welcome to POST success</p>
      <ul>
        <i>name: jxxx2 </i>
        <i>time: 20251030160012 </i>
        <i>temp: 24.2 </i>
        <i>IP: ::ffff:172.27.1.139 </i>
      </ul>
    </body>
  </html>