第21話 APIサーバ構築(GET編)
こんにちは!Kenです。
今回から実際の管理画面を作成していきたいと思います。ウェブページとサーバー側でのデータのやり取りが入ってくるのでとても重要な回となります。
まずはお客さま情報管理画面を作っていきます。
users.ejsの編集
前回header.ejs,footer.ejs,nav.ejsなどのパーシャルファイルを作成したので、これらを読み込むのと、JavaScriptファイルを読み込みましょう。JavaScriptファイルは前回main.jsと名付けましたが、わかりやすいように、users.jsと名前を変えました。
users.ejsです。
<!DOCTYPE html>
<html lang="ja">
<head>
<%- include ("../partials/header.ejs") %>
</head>
<body>
<%- include ("../partials/nav.ejs") %>
<div id='usersPage'>
<h1 class='top-font'>お客さま情報管理画面</h1>
</div>
<%- include ("../partials/footer.ejs") %>
<script src='js/users.js'></script>
</body>
</html>
main.cssです。(追加コードのみ)
/* 顧客管理画面 */
#usersPage{
text-align:center;
}
そして、Webページ /users へアクセスしてみましょう。
とりあえず真っ白なキャンバスは作れましたね。
EJSファイルはHTMLとJavaScriptを組み合わせて、ある程度までは書いていけるのですが、やっぱり限界がありますし、<%>とかを使うので書き方がめんどくさいです。ですので、私はここから先のコードは全てJavaScript(users.js)の中にコーディングしていきたいと思います。そうすることで、エラー対処がいろんなファイルに分散することなく、全てusers.js内だけで収まるのも良い点です。
フロント側のコーディング
users.jsへいきなりコーディングを始める前に、users.jsへ次のコードを書きましょう。
(()=>{
//コードはここに書いていく。
})();
これは無名関数と即時関数というもので、アロー関数形式で書いたものです。これは変数のスコープを無名関数内に限定するもので、この無名関数内に書かれた変数へはこの関数外からはアクセスすることができません。いわゆる封じ込め効果があるものです。
これからコードは全て(()=>{ と })();の間に書いていくものとします。
今回は、APIサーバを自分で構築していきます。APIはApplication Program Interfaceの略で、今回で言うとフロント側(管理画面)とサーバー側(データベース)とのやりとりを行うためのものです。なぜこれが必要かと言うと、管理画面に顧客一覧テーブルを作成するためには、データベース(PostgreSQL)から顧客データを引っ張ってこなくてはならないからです。そのフロントとサーバのインターフェースとなるプログラムを作成していきます。
APIサーバ構築を極めれば、管理画面はほぼできたも同然です。頑張りましょう!
まずはAPIサーバのアドレスを決めてしまいましょう。
const API_URL = 'https://linebot-reservation.herokuapp.com/api/';
herokuの基本アドレスの後ろに/apiをつけたものをAPIサーバのアドレスとしましょう。
users.jsはひとまずこんなコードとなります。
(()=>{
const API_URL = 'https://linebot-reservation.herokuapp.com/api/';
window.addEventListener('load',()=>{
fetchData();
});
const fetchData = async () => {
try{
const response = await fetch(API_URL);
console.log('response:',response);
const data = await response.json();
console.log('data:',data);
}catch(error){
alert('データ読み込み失敗です');
}
}
})();
(次回から無名関数と即時関数のところの表示は省略します)
画面がロードされたらAPIサーバへデータをフェッチする関数を呼びます。そのfetchDataの中身はと言うとAPI_URLへGETメソッドしており、その結果返ってくるresponseとdataをconsole.logしてあげています。fetchを使うので、もちろんasync/awaitを使いますよね。
ひとまず、console.logでしっかりresponseとdataがフロント側へ引っ張ってこれているか確認するまでを今回の目標としましょう。
ルーティングのコーディング
先ほどは、フロント側からfetchにより /apiへGETリクエストを送信するところまでをコーディングしました。では、次はそのリクエストを受け取ったサーバー側の処理を書いていきます。サーバー側の処理なので、大元のindex.jsファイルですよね。
まずはルーティングのコードを書いていきます。以前、index.ejs、users.ejs、reservations.ejs画面へのルーティングをコードしましたよね。それとは別ファイルのルーティングを作成していきましょう。
const apiRouter = require('./routers/api');
まずapiRouterなる変数を用意します。
そしてexpressのコード部分に次を追加しましょう。
.use('/',router)
.use('/api',apiRouter)
.use('/',router)は元々ありましたが、その下に.use('/api',apiRouter)を追加しました。
ではroutersフォルダの中にapi.jsを作成しましょう。
const express = require('express');
const router = express.Router();
const controller = require('../controllers/api');
router
.route('/')
.get(controller.getData);
module.exports = router;
今回は、controllerというものを導入してます。コントローラーはその名の通り、GETやPOSTで上がってきたリクエストをハンドリングする役割を持ちます。
この辺の概念はぜひ以下の動画をみて学習してみてください。私の師匠であるつよぽん先生のサイトです。
さてコードを見ると、route('/')なのでhttps://herokuアプリ名.herokuapp.com/へアクセスされた時?って勘違いされがちですが、先ほどのindex.jsで.use('/api',apiRouter)としているので、デフォルトのアドレスがhttps://herokuアプリ名.herokuapp.com/apiなのです。ですので、/apiへGETリクエストが送られた際はcontrollerがgetDataメソッドを実行するような設定です。
Controllerの実装
では、controllerの実装を行っていきましょう。
作業フォルダ直下に、controllersフォルダを作成し、その中にapi.jsファイルを新規作成します。api.jsに次のコーディングをします。
const Data = require('../models/Data');
module.exports = {
getData: (req,res) => {
Data.findData()
.then(data=>{
console.log('data in controller:',data);
res.status(200).json(data);
})
.catch(e=>console.log(e));
}
}
コントローラーの中身はシンプルにしてます。その代わり、モデルを導入し、実際の処理はその中に書いていきます。
Dataというモデルを用意し、その中のfindData()メソッドを実行し、返ってきたデータをレスポンスしてあげてます。データベースからデータを取ってくる処理なので、 Promiseですから、上記のようなコードとなってます。
作業フォルダ直下にmodelsフォルダを作成し、その中にData.jsファイルを新規作成しましょう。
Data.jsの中のfindData()メソッドでは、データベースへ接続し、usersテーブルデータとreservationsテーブルデータを取得し、それを返してあげる処理をコーディングします。
const { Client } = require('pg');
const connection = new Client({
user:process.env.PG_USER,
host:process.env.PG_HOST,
database:process.env.PG_DATABASE,
password:process.env.PG_PASSWORD,
port:5432
});
connection.connect();
module.exports = {
findData: () => {
return new Promise((resolve,reject)=>{
const pickup_users = {
text:'SELECT * FROM users;'
};
const pickup_reservations = {
text:'SELECT * FROM reservations;'
};
connection.query(pickup_users)
.then(users=>{
connection.query(pickup_reservations)
.then(reservations=>{
const data = {
users:users.rows,
reservations:reservations.rows
}
console.log('data in model:',data);
resolve(data);
})
.catch(e=>console.log(e))
})
.catch(e=>console.log(e))
});
}
}
Data.jsの中ではPostgresへ接続する必要があるため、上のような記述となってますね。そして2つのクエリを実行してあげて、それぞれusersテーブル、reservationsテーブルからデータを取得し、それらをdataオブジェクトの形にして返してあげています。
実際reservationsテーブルから全データをとるとなると、膨大となってしまうため、必要なデータのみとするような処理は必要かもしれませんが。
データの取得確認
さぁここまで実装できたらherokuへデプロイしましょう。
もうお気づきかもしれませんが、api.jsの中には、
console.log('data in controller:',data);
Data.jsの中には、
console.log('data in model:',data);
を仕込んでありますので、サーバ側でのデータ取得状況も一緒に確認し、それが最終的にフロント側のconsole.logで出力できるか確認していきましょう。
$ heroku logs --tail
でサーバ側プログラムのコンソールを開き、また、GoogleChrome側も、メニューの「表示」→「開発/管理」→「デベロッパーツール」を開いておきましょう。
ではトップページの顧客情報ボタンよりお客さま情報管理画面(すなわち /users)へアクセスしてみましょう。
画面ロードと同時にデータをフェッチするようなプログラムにしているため、アクセスした時点で結果が出るはずです。
まずサーバ側のconsole.logを確認します。
data in modelsはしっかり取れてますね。
data in controllerも大丈夫そうです。
ではこれらがしっかりフロント側へ渡っているか確認しましょう。
まず、responseの中身です。responseはただのHTTPレスポンスなので、このbodyの部分をJSONとして抽出してあげる必要があります。それが.json()メソッドです。
よって返ってきたデータの中身を見るためには、dataの中身を見る必要があります。
フロント側へもしっかりデータが返ってきていることがわかりますね。
今回はここまでとします。次回はこの返ってきたデータを使ってフロント側の構築をJavaScriptで実装していきたいと思います。
少しでも参考になりましたら「スキ」をいただけると嬉しいです。
最後までお読みいただきありがとうございました。
ご不明点等はMENTA等にてご質問いただければと思います。
この記事が気に入ったらサポートをしてみませんか?