はじめに
友人(@digital24s さん)と一緒にウェブサイトのリニューアルを検討しています。データベースを使ってコンテンツを表示するページを作りたいと相談されました。ウェブアプリでデータベースに接続する仕組を整理したいと思います。
ウェブアプリでデータベースに接続する
データベースは、Microsoft SQL Server 、Oracle Database 、PostgreSQL 、MySQL などありますが、これらはサーバ環境で実行されるソフトウェアとして提供されます。
アプリがデータベースに接続するには、データベースが用意している通信手段を使います。アプリを開発する言語環境ごとに、データベースに接続するライブラリが用意されています。
いわゆるウェブアプリは一般的に、ウェブブラウザで実行される「フロントエンドプログラム」と、ウェブサーバで実行される「バックエンドプログラム」で構成されます。
フロントエンドプログラムを実行するウェブブラウザの JavaScript エンジンは、残念なことにデータベースと通信できません。つまりウェブアプリのフロントエンドはデータベースと接続できません。ではどうするか。
ウェブアプリのフロントエンドプログラムは、ウェブサーバで動作するバックエンドプログラムと通信できます。バックエンドプログラムは、Windows や Linux などで動作していてデータベースと通信できます。そこで、フロントエンドプログラムはバックエンドプログラムを経由してデータベースと接続するようにします。
ウェブアプリでデータベースに接続するプログラムを書いてみましょう。
ウェブアプリで MySQL データベースに接続する
データベースは、ウェブサイト用レンタルサーバの多くで用意されている MySQL を使いたいと思います。
データベースに接続するバックエンドのプログラムは、Node.js 、Python 、PHP を試してみます。
- MySQL 8.0
- Node.js 24.14
- Python 3.13
- PHP 8.5
MySQL データベースを用意する
データベースサーバを導入するマシンを用意します。
MySQL データベースは Windows 、Linux 、Solaris 、macOS で動作する製品があります。
通常はサーバ機+サーバ OS を使いますが、クライアント用パソコン+Windows 11 でも動作します。
MySQL データベースを導入します。今回は無償版を使います。
WindowsにMySQLをインストールする #環境構築 - Qiita
「システム管理者(root)アカウントのパスワード」を指定しておきます。
加えて、管理ツールである MySQL Workbench を導入します。
MySQL Workbenchのインストールとサーバー接続・DB管理
さらに、サンプルデータを導入しておきましょう。
MySQLのサンプルデータベース一覧 #MySQL - Qiita
データベースサーバを動作するコンピュータと、それに接続するアプリを動作するコンピュータは、通常は別々です。
MySQL データベースをインストールした直後は、別のコンピュータから接続できるようになっていません。設定を変更します。
MySQLを外部パソコンから接続する方法 | 合同会社ウェブライト
Node.js プログラムでデータベースに接続する
Node.js プログラムでデータベースに接続してみましょう。
Node.jsアプリケーションとMySQLを接続しよう! | プログラミングの入門なら基礎から学べるProgate[プロゲート]
Node.js プログラムで MySQL に接続できる用意する
Node.js プログラムの実行環境で、MySQL に接続するライブラリを導入します。
$ npm install mysql
Node.js プログラムで MySQL に接続する
Node.js プログラムで MySQL に接続するコードを書いてみます。
const mysql = require('mysql');
// データベースに接続
const conn = mysql.createConnection({ host: "(ホスト)", user: "(ユーザ)", password: "(パスワード)", database: "sakila" });
conn.connect((err) => {
console.log("Connected.");
});
// データを取得
conn.query(
"SELECT * FROM actor",
(err, results) => {
// 結果を表示
for (const row of results) {
console.log(JSON.stringify(row));
}
}
);
// 接続を閉じる
conn.end();
ユーザおよびパスワードはコード中に記載しないのが望ましいでしょう。
エラー処理は省いています。
上記の mysql ライブラリの connect() 、query() は、順番にキューに入れられ実行される、そのため「同期的」に実行されます。
Python プログラムでデータベースに接続する
Python プログラムでデータベースに接続してみましょう。
PythonからMySQLに接続してデータベースを操作するには - TECH PLAY
Python プログラムで MySQL に接続できる用意する
Node.js プログラムの実行環境で、MySQL に接続するライブラリを導入します。
$ pip install mysql-connector-python
Python プログラムで MySQL に接続する
Python プログラムで MySQL に接続するコードを書いてみます。
import mysql.connector
# データベースに接続
conn = mysql.connector.connect(host="(ホスト)", user="(ユーザ)", password="(パスワード)", database="sakila")
# データを取得
cursor = conn.cursor()
cursor.execute("SELECT * FROM actor")
results = cursor.fetchall()
# 結果を表示
for row in results:
print(row)
# 接続を閉じる
cursor.close()
conn.close()
PHP プログラムでデータベースに接続する
PHP プログラムでデータベースに接続してみましょう。
PHPからMySQLを扱う方法を解説 - TECH PLAY
PHP プログラムで MySQL に接続できる用意する
PHP プログラムの実行環境で、PHP に接続する機能「PDO(PHP Data Objects)」を有効にします。
Node.js や Python は必要なライブラリをアプリに組込していますが、PHP は実行環境に必要なドライバが組込されます。
(前略)
extension=php_pdo.dll
extension=php_pdo_mysql.dll
(後略)
上記の設定は Windows 版 PHP エンジンの場合です。
PHP プログラムで MySQL に接続する
PHP プログラムで MySQL に接続するコードを書いてみます。
<?php
// データベースに接続
$pdo = new PDO("mysql:host=(ホスト); dbname=sakila", "(ユーザ)", "(パスワード)");
print "Connected.\n";
// データを取得
$stmt = $pdo->query("SELECT * FROM actor");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 結果を表示
foreach ($results as $row) {
print(json_encode($row) . "\n");
}
フロントエンドプログラムを用意する
クライアントのウェブブラウザで実行するフロントエンドプログラムを用意しましょう。
【JavaScript】Fetch API入門|GETでのデータ取得とPOSTでの送信 | エンベーダー
ウェブサーバのバックエンドプログラムと通信するのに、JavaScript の fetch() API を使ってみます。
(前略)
<button id="get">取得</button>
<ul id="list"></ul>
<div id="result"></div>
(後略)
document.querySelector('#get').addEventListener('click', () => {
// サーバから取得
fetch("/actor/list")
.then((res) => {
return res.json();
})
.then((data) => {
// 取得したデータを表示
const list = document.querySelector('#list');
list.innerHTML = "";
data.forEach((item) => {
const elem = document.createElement('li');
elem.innerHTML = `
<span>(${item.actor_id}) ${item.first_name} ${item.last_name}</span>
`;
list.appendChild(elem);
});
document.querySelector('#result').innerHTML = "Succeeded.";
})
.catch((err) => {
document.querySelector('#result').innerHTML = "Failed: " + err.message;
});
});
fetch() に接続先のサーバを指定しますが、上記のクライアントプログラムを設置するウェブサーバと、接続先のバックエンドプログラムが実行されるウェブサーバを同じにして、ドメインを省略します。
パスは /actor/list を指定することにします。
バックエンドプログラムから返されるオブジェクトは、actor_id 、first_name 、last_name フィールドを持つオブジェクトの配列になるようにします。
バックエンドプログラムを用意する
続いて、ウェブサーバで実行するバックエンドプログラムを用意しましょう。
上記のクライアントプログラムを用意したときに決めたように、パス /actor/list を指定されたとき、actor_id 、first_name 、last_name フィールドを持つオブジェクトの配列を返すようにします。
Node.js でバックエンドプログラムを用意する
Node.js を使ってバックエンドプログラムを用意します。Express フレームワークを使いたいと思います。
【Node.js】Express.jsを使う場合と使わない場合でWeb API作成方法を比較してみた | DevelopersIO
Express フレームワークを利用できる用意する
Node.js プログラムの実行環境で、Express フレームワークを導入します。
$ npm install express
Express フレームワークでバックエンドプログラムを用意する
Express フレームワークを使ってバックエンドの処理するコードを書いてみます。
(前略)
const express = require('express');
const app = express();
app.get("/", (req, res) => {
res.sendFile(__dirname + "/client.html");
});
app.get("/actor/list", (req, res) => {
// データベースに接続
const conn = mysql.createConnection({ host: "(ホスト)", user: "(ユーザ)", password: "(パスワード)", database: "sakila" });
conn.connect((err) => {
console.log("Connected.");
});
// データを取得
conn.query(
"SELECT actor_id, first_name, last_name, last_update FROM actor",
(err, results) => {
// 結果を返す
res.json(results);
}
);
// 接続を閉じる
conn.end();
});
app.listen(8000);
パス / を指定されたときは client.html を返すようにしています。
エラー処理は省いています。
上記のプログラムを実行して、フロントエンドプログラムからリクエストされるのを待機します。
$ node server.js
データベースの接続(コネクション)を使い回す
上記のプログラムは、フロントエンドからリクエストがある度に、データベースに接続して処理し終えると切断しています。これはこれで問題ないものの、処理の負荷が大きいので望ましくありません。データベースに接続するのはバックエンドプログラムが開始した最初だけにして、リクエストに応じて処理するときはその接続を使い回すようにしたい。
一つの接続(コネクション)を使いまわしてもいいけれど、エラー発生して接続が切れた場合にバックエンドプログラムを起動し直さないと処理できなくなります。そこでコネクションプールを使うようにします。
Express プログラムでデータベースの接続を使い回す
上記のバックエンドプログラムを、コネクションプールを使うよう書換してみます。
Node.js + MySQLでコネクションプールを扱う方法|いけだい|Webエンジニア|coconalaブログ
// データベースに接続してコネクションプールを用意
const pool = mysql.createPool({ host: "(ホスト)", user: "(ユーザ)", password: "(パスワード)", database: "sakila" });
app.get("/actor/list", (req, res) => {
// プールから接続を取得
pool.getConnection((err, conn) => {
// データを取得
conn.query(
"SELECT actor_id, first_name, last_name, last_update FROM actor",
(err, results) => {
// 接続をプールに戻す
conn.release();
// 結果を返す
res.json(results);
}
);
});
});
Python でバックエンドプログラムを用意する
Python を使ってバックエンドプログラムを用意します。FastAPI フレームワークを使いたいと思います。
FastAPIを用いたAPI開発テンプレート #Python - Qiita
FastAPI フレームワークを利用できる用意する
Python プログラムの実行環境で、FastAPI フレームワークを導入します。
$ pip install fastapi
FastAPI フレームワークでバックエンドプログラムを用意する
FastAPI フレームワークを使ってバックエンドの処理するコードを書いてみます。
import mysql.connector
import fastapi
import uvicorn
app = fastapi.FastAPI()
@app.get("/")
async def root():
return fastapi.responses.FileResponse("client.html")
@app.get("/actor/list")
async def list():
# データベースに接続
conn = mysql.connector.connect(host="(ホスト)", user="(ユーザ)", password="(パスワード)", database="sakila")
# データを取得
cursor = conn.cursor(dictionary=True) # 辞書形式で結果を取得する
cursor.execute("SELECT actor_id, first_name, last_name, last_update FROM actor")
results = cursor.fetchall()
# 接続を閉じる
cursor.close()
conn.close()
# 結果を返す
return results
uvicorn.run(app, host="0.0.0.0", port=8000)
上記のプログラムを実行して、フロントエンドプログラムからリクエストされるのを待機します。
$ python server.py
FastAPI プログラムでデータベースの接続を使い回す
上記のバックエンドプログラムを、コネクションプールを使うよう書換してみます。
Python + MySQL でコネクションプーリング #Python - Qiita
# データベースに接続してコネクションプールを用意
pool = mysql.connector.pooling.MySQLConnectionPool(host="(ホスト)", user="(ユーザ)", password="(パスワード)", database="sakila")
@app.get("/actor/list")
async def list():
# プールから接続を取得
conn = pool.get_connection();
(中略)
# 接続をプールに返す
cursor.close()
conn.close()
# 結果を返す
return results
PHP でバックエンドプログラムを用意する
PHP を使ってバックエンドプログラムを用意します。Slim フレームワークを使いたいと思います。
Slim フレームワークを利用できる用意する
PHP プログラムの実行環境で、Slim フレームワークを導入します。
軽量フレームワーク Slimを試す - インストール編 - arms inc. Engineers' Blog
$ composer require slim/slim
$ composer require slim/psr7
Slim フレームワークでバックエンドプログラムを用意する
Slim フレームワークを使ってバックエンドの処理するコードを書いてみます。
<?php
require __DIR__ . '/vendor/autoload.php';
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
$app = AppFactory::create();
$app->get("/", function (Request $request, Response $response) {
$response->getBody()->write(file_get_contents(__DIR__ . "/client.html"));
return $response;
});
$app->get("/actor/list", function (Request $request, Response $response) {
// データベースに接続
$pdo = new PDO("mysql:host=(ホスト); dbname=sakila", "(ユーザ)", "(パスワード)");
// データを取得
$stmt = $pdo->query("SELECT actor_id, first_name, last_name, last_update FROM actor");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 結果を返す
$payload = json_encode($results, JSON_UNESCAPED_UNICODE);
$response->getBody()->write($payload);
return $response;
});
$app->run();
上記のプログラムを実行して、フロントエンドプログラムからリクエストされるのを待機します。
Node.js や Python のバックエンドプログラムは、自分がウェブサーバになってリクエストを待って対応します。PHP のプログラムは、Apache などのウェブサーバがリクエストを受けると呼出されます。
上記のプログラムを Apache+PHP の環境で運用するには、ドキュメントルートのディレクトリに配置して、そのディレクトリに以下の設定します。
# すべてのリクエストを server.php へ送る
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ server.php [QSA,L]
# ファイルの指定がないときは server.php を呼出
DirectoryIndex server.php
Apache 環境は mod_rewrite が有効である必要あります。
PHP プログラムでデータベースの接続を使い回す
Node.js や Python のバックエンドプログラムは、プログラムが起動したままフロントエンドのリクエストを待って処理します。そのため、プログラムの開始で接続しておいて次のリクエストで使い回すことができます。ところが PHP のバックエンドプログラムは、フロントエンドのリクエストがあった都度、ウェブサーバによって起動される仕組です。そのため、プログラムの開始で接続しておいて次のリクエストで使い回そうとしてもプログラムは終了してしまうので使えず、次のリクエストで改めてプログラムが開始して接続し直すことになります。
そこで、PDO に「持続(永続)的接続」が用意されています。
PDO接続は毎回作るべき?接続プールと同時アクセスの本当の問題 #MySQL - Qiita
(前略)
// データベースに接続
$pdo = new PDO("mysql:host=(ホスト); dbname=sakila", "(ユーザ)", "(パスワード)", [
PDO::ATTR_PERSISTENT => true // 持続的接続を指定
]);
(後略)
持続的接続を指定して PDO を生成すると、プログラムが終了しても PHP エンジンが接続を残しておいて、次にプログラムが実行されたとき事前の接続を利用するようになります。
これは、上記の Node.js や Python のプログラムで利用した「コネクションプール」と違います。コネクションプールは複数の接続を用意して使いますが、PDO の持続的接続は一つの接続を使い回します。
また、ウェブサーバが PHP プログラムを呼出するのに「(Apache の)mod_php」「(従来の)CGI」「FastCGI」「PHP-FPM」の方式がありますが、「(従来の)CGI」方式で実行しているとプログラムの実行の度にプロセスが作成し直され、「持続的接続」を指定していてもプロセスの終了と共に接続が切られてしまい、使い回すことになりません。
自分が利用しているレンタルサーバを確認すると、「(従来の)CGI」方式で実行されるようで、PDO の持続的接続できませんでした。


