第4章 WEB/APPサーバーの設計(Nginx, Node.js)

4-1. WEB&アプリサーバーの設計
4-2. Nginx 初期セットアップ
4-3. SSLサーバー証明書の取得と自動更新
4-4. Node.js サーバーの初期設定
4-5. はじめてのAPIプログラミング
4-6. ExpressフレームワークでAPIプログラミング
4-7. バックエンドAPIアプリの永続化
4-8. 天気を取得するAPIアプリを実行する

WEBアプリは、スマホやPCのアプリ画面(WEBフロントエンド)、データを処理するしくみ(バックエンドAPIアプリ)、そしてデータを保存する場所(データベース)の3つの役割に分かれています。
バックエンドAPIアプリには、たとえばログイン機能(/login)やユーザー情報を検索する機能(/users)などの機能を持たせることができます。API=サーバー側で動くアプリ。
本章では、まず「こんにちは」といったメッセージを返す簡単な機能から始めて、少しずつデータベースに接続して情報を読み出すしくみまでをコーディングで体験します。

前提:WEB/APPサーバーは、ドメイン・DNSが設定されていること。つまり、Linuxユーザー名とドメイン名を使って、SSH接続できる状態になっていること。

4-1. WEB/APPサーバーの設計

本学習プロジェクトでは、WEBフロントエンドとバックエンドAPIサーバーは1台のLinuxサーバーにまとめてホスティングします。
そのため、バックエンドAPIサーバーを準備する前に、WEBサーバーのHTTPSによる暗号化通信を先に設定する方が簡単かつ実用的です。
さらに1つのWEBサーバーに複数のWEBサイトをホスティングするマルチサイト設定も体験します。
なお、WEB&アプリサーバーのことを略して「WEB/APPサーバー」と表記する場合もあります。APPはApplicationの略ですね。

WEB&アプリサーバーの構成

  • WEBサーバーには、Nginxという、WEBページを配信するためのソフトウェアをインストールします
  • APIサーバーには、Node.jsという、データを処理するアプリ用のソフトウェアをインストールします

4-2. Nginx 初期セットアップ

Nginx のインストール

$ sudo apt update
$ sudo apt install nginx

Nginx 起動と自動起動

$ sudo systemctl start nginx
$ sudo systemctl enable nginx

動作確認

$ curl http://localhost

# 成功するとhtmlソースが表示される
<html><title>welcome to nginx! ...

マルチWEBサイトを設定する

$ sudo nano /etc/nginx/sites-available/ドメイン名(例:web.hogehoge.com)

# ファイルに追記する設定値
server {
    listen 80;
    server_name web.hogehoge.com;
    location /site1 {
        root /home/ユーザ名/www1;
        index index.html;
    }
    location /site2 {
        root /home/ユーザ名/www2;
        index index.html;
    }
}

Nginx ユーザ(www-data)が wwwディレクトリにアクセスできるようにする

#サブディレクトリ含め全て[rwx,r-x,r-x] 
$ sudo chmod -R 755 /home/ユーザ名/www

# 親フォルダにも実行権限を追加 
$ sudo chmod 701 /home/ユーザ名

# 所有者を追加 
$ sudo chown -R ユーザ名:www-data /home/ユーザ名/www

WEBを有効化する

# 該当サイトが表示されるか検査 
$ ls -l /etc/nginx/sites-enabled/

# 無効ならば 
$ sudo ln -s /etc/nginx/sites-available/web.hogehoge.com /etc/nginx/sites-enabled

# Nginxを再起動する 
$ sudo systemctl reload nginx

4-3. SSLサーバー証明書の取得と自動更新

前提:インターネット公開済みサーバー。ドメイン取得&DNS設定済み。TCP80(http)でアクセスできること。

無料 Let’s Encrypt を使う。

Let’s Encrypt のクライアント[certbot]をインストール

$ sudo apt install certbot python3-certbot-nginx -y

Nginx 設定を更新

$ sudo nano /etc/nginx/sites-available/default

ファイルの中身

server {
    listen 80;
    server_name hogehoge.com www.hogehoge.com;
    root /home/ユーザ名/www;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}

Nginx 再起動

$ sudo systemctl restart nginx

TLS証明書を取得

$ sudo certbot --nginx -d hogehoge.com -d www.hogehoge.com

自動更新できるか検査 

$ sudo certbot renew --dry-run

# 応答例:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/ome.funnygeekjp.com.conf
Simulating renewal of an existing certificate for ome.funnygeekjp.com
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/ome.funnygeekjp.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

念のため更新日時も確認 

$ systemctl list-timers | grep certbot

応答例:
Mon 2025-08-25 03:51:58 JST 5h 55min Sun 2025-08-24 21:53:43 JST  3min 1s ago certbot.timer certbot.service

証明書の存在を検査

$ cd /etc/letsencrypt/live/aaa.hogehoge.com/

cert.pem	サーバー証明書
chain.pem	中間証明書
fullchain.pem	サーバー証明書と中間証明書を結合したもの
privkey.pem	秘密鍵

Nginx 設定ファイルにSSL証明書の場所を教える

$ sudo nano /etc/nginx/sites-available/aaa.hogehoge.com

追加する内容:

server {
    listen 443 ssl;
    server_name aa.hogehoge.com;
    ssl_certificate /etc/letsencrypt/live/aa.hogehoge.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/aa.hogehoge.com/privkey.pem;
    root /var/www/html;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}

TCP80→443 リダイレクトも追加する:

server {
    listen 80;
    server_name ome.funnygeekjp.com;
    return 301 https://$host$request_uri;
}

4-4. Node.js サーバーの初期設定

OSパッケージを更新

$ sudo apt update && sudo apt upgrade -y

nvm (node Version Manager)をダウンロードしてインストールする

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

インストールできたことを検査する

$ nvm --version      # 例えば 0.40.3が表示される

Node.jsをダウンロードしてインストールする

$ nvm install 24

Downloading and installing node v24.7.0...

Node.jsのバージョンを確認する

$ node -v      # 例えば "v24.7.0"が表示される。

npmのバージョンを確認する

$ npm -v          # 例えば "11.5.1"が表示される。

4-5. はじめてのAPIプログラミング

新しいファイル[hello.js]を作成し、下記コードを記述します。ポート番号は、任意設定し、ファイアウォールで許可すること。

$ cd ~/app
$ nano hello.js

// Node.js の HTTPモジュールをインポート
const http = require('http');

// Appサーバーを作成
const server = http.createServer((req, res) => {
    res.statusCode = 200;                      // HTTPステータスコード(成功: 200)
    res.setHeader('Content-Type', 'text/html; charset=utf-8;'); // 応答するコンテンツタイプ:
    res.end('こんにちは。かのです。');                // レスポンスに文字列を返す
});

// サーバーをポート3001で起動
const PORT = 3001;
server.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}/`);
});

Node.js で はじめてのAPIを起動します。

$ cd ~/app
$ node hello.js

ブラウザでAPIにアクセスします

http://サーバーのIPアドレス:3001/
もしくは
http://サブドメイン:3001/

成功した場合の例

APIを改造

URLのクエリパラメータ[/?text=”たらーん”]を取得して、テキストの値を処理できることを体験します。
http.createServer() 関数を下記の通り改造します。

// Appサーバーを作成
const server = http.createServer((req, res) => {
    const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
    const text = parsedUrl.searchParams.get('text') || '(テキストなし)';  // GETメソッドを使う
    res.statusCode = 200;                            // HTTPステータスコード(成功: 200)
    res.setHeader('Content-Type', 'text/html; charset=utf-8;');       // 応答するコンテンツタイプ(text/html)と文字コード:
    res.write(`${text}\n`);
    res.end('こんにちは。かのです。'); // レスポンスに文字列を返す
});

ブラウザのURLでクエリパラメータを指定してAPIにアクセスします

http://サーバーのIPアドレス:3001/?text="たらーん"
もしくは
http://サブドメイン:3001/?text="たらーん"

成功した場合の例

4-6. ExpressフレームワークでAPIプログラミング

Expressは、Node.jsでWebアプリやAPIを作るときに使う便利なツール(フレームワーク)です。
たとえば「このURLにアクセスされたらこう返す」「このデータを受け取って保存する」といった処理を、シンプルなコードで書けるようになります。

Expressモジュールをインストールします

$ npm init -y
$ npm install express

新しいファイル[hello-express.js]を作成し、下記コードを記述します

$ cd ~/app
$ nano hello-express.js

// 必要なモジュールを読み込む
const express = require('express');
const app = express();

// ルート "/" にアクセスしたときのレスポンス
app.get('/', (req, res) => {
  res.send('いえーい');
});

// サーバーをポート3000で起動
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`API待ち受け http://localhost:${PORT}/`);
});

Node.js で はじめてのAPIを起動します。

$ cd ~/app
$ node hello-express.js

ブラウザでAPIにアクセスします

APIを改造

URI(Uniform Resource Identifier)を追加します。Web上の機能を表すものです。
Webブラウザやアプリは、このURIを使って目的のデータやサービスにアクセスします。
URIには、プロトコル・ホスト名・パス・クエリなどが含まれ、これらを組み合わせてリソースを一意に指定します。

app.get()関数を追加します。

app.get('/weather', (req, res) => {
  const weatherOptions = ['晴れ', 'くもり', '雨'];
  const randomWeather = weatherOptions[Math.floor(Math.random() * weatherOptions.length)];
  res.send(randomWeather);
});

ブラウザで追加したURIにアクセスします。何度かリロードして、表示されるテキストが変わることを検査します

app.get()関数を少しだけ解説

app.get()はExpressモジュールの機能です。HTTPのGETリクエストを処理するルーティングメソッドのこと。

app.get(‘/’, (req, res) => { }); という形式で記述し、reqが入力、resが出力。RequestとResponseですね。

  • res.send()は、データをまとめて送信し、自動終了する。
  • res.write()は、データを分割して送信できる。複数回実行できる。res.end()と組み合わせないとレスポンスが終了しない。
  • res.end()は、データを送信しつつ、レスポンスを終了する。

例:

app.get('/hello', (req, res) => {
  res.send('こんにちは!');    // これ1行でOK
});

app.get('/stream', (req, res) => {
  res.write('ページの先頭\n');  // 書いて
  res.write('途中の内容\n');   // 書いて
  res.end('最後の行');      // ここで終了
});

レスポンスの応用例:

  • JSON形式で送る: 
res.send({ message: 'こんにちは', status: 200 });
  • 配列で送る: 
res.send(['Apple', 'Banana', 'Orange']);
  • 画像などバイナリデータを送る: 
const fs = require('fs');
const file = fs.readFileSync('./image.png');
res.send(file); // バイナリデータとして送信
  • HTML形式で送る: 
res.send('<h1>こんにちは</h1><p>ここがメッセージです</p>');
  • ステータスコードと一緒に送る: 
res.status(404).send('Not Found');
  • 変数を送る
res.write( `${req.query.text}\n`);

4-7. バックエンドAPIアプリの永続化

APIアプリを毎回[$ node app.js]で起動していると、APIアプリを止めている間は、使いたいときに使えません。
プロセス管理[pm2]を使うと、アプリを永続的に実行できます。アプリをバックグランドで動作させたり、OS再起動後も自動起動したりできます。

$ npm install -g pm2           # pm2のインストール
$ pm2 start app.js --name my-app    # app.jsというコードを、my-appという名前で起動
$ pm2 save                # 現在の設定を保存
$ pm2 startup              # OS起動時にpm2を自動起動する

アプリのコードを変更したら再起動が必要です。

$ pm2 restart my-app

pm2で起動しているアプリの一覧を表示する

$ pm2 list

アプリを停止する

$ pm2 stop my-app   # 名前で停止
$ pm2 stop 0        # プロセスIDで停止

pm2によるアプリの起動を削除する。

$ pm2 delete my-app  # 指定したアプリを削除
$ pm2 delete all     # 全て削除

4-8. 天気を取得するAPIアプリを実行する

const express = require('express');
const axios = require('axios');
const app = express();

// weathercode を日本語に変換するマップ
const weatherMap = {
  0: '快晴☀️',
  1: '晴れ☀️',
  2: '薄曇り⛅',
  3: '曇り☁️',
  45: '霧',
  48: '霧(霧雨)',
  51: '弱い霧雨',
  53: '中程度の霧雨',
  55: '強い霧雨',
  61: '弱い雨🌂',
  63: '中程度の雨☂️',
  65: '強い雨☔',
  71: '弱い雪',
  73: '中程度の雪',
  75: '強い雪',
  80: 'にわか雨🌧️',
  81: '強いにわか雨⛈️',
  82: '激しいにわか雨⛈️',
  95: '雷雨',
  96: '雷雨(弱い雹)',
  99: '雷雨(強い雹)'
};

app.get('/weather', async (req, res) => {
  try {
    const latitude = 35.789;
    const longitude = 139.263;
    const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current_weather=true`;

    const response = await axios.get(url);
    const weather = response.data.current_weather;

    const description = weatherMap[weather.weathercode] || '不明';
    res.send(`天気: ${description}, 気温: ${weather.temperature}℃`);
  } catch (error) {
    res.status(500).send('天気情報の取得に失敗しました');
  }
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`API待ち受け http://localhost:${PORT}/`);
});

この章は以上です

コメントを残す