お前、まだ書いてたんか
お前は、まだこれを書いてたんか! と叱られそうですが、1記事で終わらそうと書き始めて、初回はHTMLとCSSまでで挫折。
2回目の記事はJavaScriptのみで疲れて終了させるという醜態を晒してしまいました。
あれだけの記事でも、3時間くらい浪費したんや。Qiitaは茶の一杯すら、でえへん。
初回の書き出しでも言いましたが、各言語のシンタックスや属性、各ファンクションの機能とか、どう書けば良いみたいな技術的詳細、ライブラリの使い方なんかは、山ほどリファレンスがある上に、ChatGPT教諭に聞けば、コードごと教えてくれるので、自分のようなヒヨコが書く必要もないよねと。
それよりも、プログラミングを勉強してたら、なんか理解がもやっとしてへん?
分かったような、分かってないような、で、何となくコードも書けてるし、動いてるし、みたいな。
で、何かある根本的な事を理解した瞬間に、視界が急にくっきりするような瞬間が、新しい事柄を勉強しているとあると思うねんけど、自分にとってそういう体験を卵どもにも共有したいと思い、書き始めたわけです。で、今回は、サーバーサイドの話。
そもそも、長い記事って、読むのも疲れるんで、逆にこれくらいのボリュームが良いかなと開き直りましたDeath。
サーバーサイドの闇
まだCSSが産まれる前、HTMLでスタイリングまでやっていた時代にウェブのフロントエンドをやっていた自分からすると、バックエンドやサーバー周りって、闇が深いというか、なんか深い井戸の底を覗き込むような、マッドなプログラマー連中が蠢いているアンタッチャブルなイメージでありました。
それではいかんと思い、JavaScriptやPearl、SQLなどを勉強していたのですが、一番最初の記事に書いたように、プログラミング用語を日本語に訳した複雑怪奇な専門用語群、複雑で抽象的なデータベース構造、無限ループやコールバックヘルなどに巻き込まれ、あえなく討死致しました。
しかし、20年ぶりに帰ってきてみると、汚泥で満ちていた道頓堀川には清流が流れ、鮎が遡上しているように、カオスと混乱に満ちた場所はコンクリートで護岸され、安全策が設けられ、立ちションすら出来ないようになっていたのです。前置き長い。
Node.js - サーバーは何をやっているのか
サーバーサイドのプログラミングは色々と環境があるわけですが、コロナで仕事も貯金も全て失っていた時に、初期アマゾンのエンジニアでアメリカITバブルの時に億万長者になった元エンジニアの以前のボスから、PHP/Laravelサイトを、nodejsサイトにリニューアルする仕事と、パソコン一式を与えられ、そこから今の独学プログラマー生活が始まったわけなので、サーバー側はいつもnode.jsがコーディングツールです。JavaScriptでフロントもバックも書けるって、単純に良いよね。
で、WEBにおけるサーバーは結局何をやっているかというと、特定のuri宛に来たリクエストを、所定のエンドポイントに渡す交通整理と、そのリクエストを料理して送り返す、セントラルキッチンみたいな役割をしている。
サーバー側で行われている事は、勿論フロントエンドにはブラックボックスである。
フロント側は、ただ注文と注文先の住所を送るだけである。で、あら不思議、注文したものがまたフロント側に返ってくるのである。
この注文の出し方と受け取り方を、HTTPといい、注文の中身や料理された飯は、HTTP requestとresponse というオブジェクトの形になって送受信される。
下に簡単なNode.js環境で、expressミドルウェアを使った、サーバーのコードを書いてみる。
// Import modules and initialized
const express = require('express');
const app = express();
const port = 3000;
// Middleware for parsing JSON bodies
app.use(express.json());
// Create function to fetch weather today from outside API
async function getWeather(address) {
// Another fetch function here to call outside API
return 'Sunny'; // Placeholder return
}
// Create API endpoint
// GET Request
app.get('/api1/', (req, res) => {
res.send('How are you?');
});
// POST Request
app.post('/api1/', async (req, res) => {
const address = req.body.address;
try {
const weather = await getWeather(address); // Replace with your weather API call
res.json({ weather });
} catch (error) {
res.status(500).send('Error fetching weather');
}
});
// Run server!
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
メチャ簡単ではないであろうか?
https://www.orenosite.com/api1/
にGETリクエストを送ると(ただuriにアクセスするだけ)、"How are you?" の文字列が返ってきて、同じアドレスにPOSTリクエストを住所と共に送ると、今現在のその住所の天気が返ってくるという、すごくシンプルなAPIである。
- 最初に必要なモジュール類や、変数なんかをガサっと読み込んで、初期化しておく。
- 前の記事に書いたけど、サーバーへのデータの送受信はJSONっていうオブジェクトを使うんで、受け取ったJSONをJavaScriptで扱えるオブジェクトに変換。
- コード内で使うファンクションを作る。
- apiエンドポイントの設定。uriと、HTTPリクエストの種類を書いて、そこにリクエストが来たら、するべき処理をJavaScriptで書く。
- 最後の塊は、ポート番号を指定して、サーバーを起動。
コードが動いているサーバーの、指定したポートにリクエストが飛んでくると、サーバーが交通整理をして、所定のuriにリクエストを届けてくれる、で、単純にページをGETしたいだけなら、ブラウザにHTMLを送り返すし、APIとして、何らかのデータを送り返す必要があるなら、サーバー側で処理をして、JSONなり何なりでブラウザに成果物を返してくれる。この例の場合は、外部APIから天気情報を取得して、フロント側に送信している。
通常のサイトにアクセスする場合、例えば、google.com とかにアクセスすると、最初のindex.htmlなりが返ってくるわけだけど、あれはAPIとは、あまり言わない。なぜかというと、静的なhtmlファイルを返しているだけで、データの処理や受け渡し的な仕事はしていないからだ。
node.jsの場合は、静的なページファイルは、"public"という所定のディレクトリに入れておくと、expressが処理してくれる。
サーバー側では、基本、静的ファイルと動的なファイルは分けられている。
いつもの様に、自分がメチャ役に立つと思った、独学用リファレンスをリンクしておくよ。
ネットニンジャは、いつも新しいテクノロジーを勉強したい時に、最初に頼るメディア。本当にわかりやすいと思う。
少し基礎知識がついてきたら、Traversyのコンテンツが神
フロントエンドとサーバーの違い
フロント側もサーバーも、同じJavaScriptで書いてるわけで、初学者は何がどう違うねんと思うであろう。
サーバー側は、フロントとは役割が全く違うので、違った仕事をしている。
データーベースからデーターをフェッチしてきてフロント側に送ったり、パスワードをハッシュで切り刻んだ挙句、塩や胡椒をかけてみたり、暗号化されたデーターを解読してみたりと、本当に色々やっている。
例えば、データーベースに問い合わせして、ユーザーの情報を送り返すコード。
const express = require('express');
const mongoose = require('mongoose');
const app = express();
const port = 3000;
app.use(express.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/databaseURI', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Could not connect to MongoDB...', err));
// Define a User model
const User = mongoose.model('User', new mongoose.Schema({
name: String,
id: String,
// other user information
}));
// Get user data then send back to frontend
app.get('/user/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).send('User not found');
res.send(user);
} catch (error) {
res.status(500).send('Error fetching user');
}
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
色々端折ってるけど、だいたいこんな感じのコード。
https://www.orenosite/user/asdfghjk
にブラウザからGET Requestが来たら、IDを元にuser(ユーザー情報)を取ってきて、それをres.send(user) でブラウザ側に返している。
なぜ、フロント側から、直接データをfetch()しないのか、直接データベースにアクセスしたらええやん。 と私も卵の初期段階の時は思っておりました。
よく考えて欲しい、フロント側のコードは丸見えである。裸である。
ガラス張りの家で、色々とあれこれ出来るであろうか?無理である。
サーバー側のコードは、見えないし、フロントからは何をやっているのか分からない。完全防備の城である。
なので、ブラックボックスの中でデリケートな処理は全てやるのである。まあ、サーバーがハックされたらあれだけどね。
AuthorizationやTokenについて、メチャわかりやすかった。
データベース
元々node.jsからバックエンドに入ったので、相性が良いとされるmongoDBを愛用しておった。
昔にやっていたSQLは複雑で頭が硬く、もう2度と会いたくないと思ってたんだけど、SQLをエクセルのテーブルだとすると、mongoDBはスケッチブックであり、複雑なテーブル設定などせずに、気軽に色々と書いていける。で、これなら挫折せずにやれると思ったわけだ。ありがとうmongo。SQLと非SQL系のデータベースについては、以前に記事を書きました。全然読まれてないけどね。
Firebase最強説
と、色々と書いてきたんだけど、サーバー側の処理は複雑である。
例えば、ユーザーのサインイン、ログイン、authentication、authorizeの処理を考えると、ユーザーから送られてきた裸のパスワードにハッシュをかける、ソルト&ペッパーをかける、JWTでトーケンを発行する、ユーザー情報をデーターベースに保存する、サーバーの環境変数に保存したシークレットで暗号をかける、ブラウザに送り返して、トーケンをRequest headerに入れる、ログインしてきたユーザーのハッシュを照合、アクセスがある度に、トーケンをディクリプトし、ユーザー情報を照合し、みたいな感じで、かなりややこしい。
因みに、何でハッシュっていうか知ってるか?ハッシュドポテトや、ハッシュのコーンビーフと同じハッシュで、ナイフで切り刻むっていう意味やで。#のマークも、包丁で刻まれた様子に似ているから。多分やけど。。。
だから、ハッシュは不可逆なんや。ハッシュしたコーンビーフを、牛に戻すのは不可能やん。
また、データベースにしても、スキーマの定義とか、モデルを作ったりとかジャマくさい。
それらの作業を全部やらなあかん。こんな人生は嫌だ。 と思っていたんだけど、Firebaseを使うと、これらほぼ全てのバックエンド業務をやっってくれる。
ユーザーの認証作業、データベースのCRUDE処理、画像やムービーを保存するバケット。全部がパッケージになっている。
また、モジュールを使うと、フロントから直接データにアクセス出来るし、APIを作りたい時は、Firebase Funcstionsで幾らでも制作可能。
自分の様な、身寄りのない自家デベロッパーには最高のバックエンドツールであり、作業時間は半分以上削減された。
Googleのサービスなんで、勿論Fluttterとかのフレームワークとの相性もバッチリ。
自分の様な野良デベロッパーよりも、世界でも変態中の変態が集まるGoogleのチームに任せた方が良いに決まっている。
個人で開発してる人には、ほんまおすすめ。
Firebaseで何が出来るか、このチュートリアルを一通りやったら、マスター出来るよ。
ここまで書いて、すでに2時間以上を浪費している。。金曜日やのに、はよ帰りたい。
HTTPとサーバーのところで、Postmanとかのツールについても書きたかったんやけど、もう集中力が無理や。
次は、また気が向いたら書くで。もしくは、ここに書き足すで。
[大幅加筆]帰ってきたで 12/10/23
書こうかと思って集中力が無理になって、帰宅したと思ったら、Qiitaの何とかカレンダーとかでPostmanが入っており、景品に釣られて、帰って参りました。
ちょうどサーバーの話とかAPIについて書いていたので、ここに書き足すのがベストと思い、続きを書くで。
なぜ全世界2,800万人以上の開発者に使われているAPIプラットフォームPostmanを使わないといけないのか?
自分の場合は、色々なきっかけでNode.jsから20年ぶりにプログラミングの勉強机に戻ってきたのですが、ある程度サーバーサイドのコードが書ける様になり、さあ、この自作APIをまんまと叩いてこましたろ! と思ったは良いのですが、そもそもAPIというのは、フロント側からリクエストを受け取り、さらにレスポンスをフロントに送り返すことで、その成果というか、機能の全貌が明らかになるわけで、これを試すためには、フロント側を作らんとあかんやないか! と絶望するわけです。
一応、開発中はconsole.logとかでレスポンスや処理中データをちまちま確認しながらやったりするわけですが、何かあるたびに、コードの中にデバッグの為だけのconsoleコードを挿入し、結果、コードが煩雑になり、結果どれがどのconsole.logだったのか訳が分からなくなり、吐き出される変数や、エラーメッセージの波の呑まれるコンソール廃人となっていくわけです。
また、リクエストが成功したのかどうなのかのステータスやエラーの詳細、さらに、ちゃんとログインして認証されているユーザーのみAPIにアクセスできる様にしたりとか、フォームでアップロードされた画像やファイルなんかをサーバー側で処理した上でフロント側に返すとか、そういう機能がちゃんと動いてるのか、フロント側を作らずにどうやってテストすれば良いのか途方に暮れてしまうわけです。いや、実際にオレは途方にくれました。
また、APIのリクエストには、Get/Post/Delete/Patch など各種あるわけで、こういったものを、それぞれどうテストすれば良いのか?
せっかく数ヶ月も費やしてサーバーサイドのプログラミングをやったと思ったら、次はまたCSSとかHTMLとか、さらにはReactやVueみたいなフレームワークまでやらんといかんのかと思うと、もうええわ、プログラミングとか変人の趣味 などと呟いて挫折してしまうかもしれないわけです。
で、Postmanですよ。
せっせと自分の作ったローカル/プロダクションのAPIを叩いて、そのレスポンスやステータス、エラーの詳細など、知りたい部分のほとんどを返してくれる訳ですよ。
また、Headerにトーケンなどを設定しておけば、ログイン・ログアウトのシュミレーションも出来るし、画像ファイルやJSONファイルなんかもRequest bodyに入れて送れるので、フォームのシュミレーションも出来るし、サーバー側のテストをフロント側を作らなくても、出来てしまうという凄いマンなのです。
ウェブ版、デスクトップ版、VS CODEのエクステンションバージョンなど、色々なフォーマットで出ているので、自分の使いやすいものを選べば良い。
個人的には、VS CODEとシームレスで繋がっているエクステのバージョンを使ってます。
とりあえず、簡単なAPIを作って、実際にPostmanをどう使うのか、特別にお前らに解説してやろう。
APIを作り、Postmanを使う
1、NodeのプロジェクトをVS Codeで作る。
プロジェクト用のディレクトリを作ったら、Terminal窓で以下のコマンドでプロジェクトを初期化。始まりはいつも npm init.
npm init
2、次にexpressモジュールとモジュール類をインストール
npm install express
いちいちリスタートするのがめんどいので、nodemonもDev環境にインストールしておきましょう。あと、nodemonスタートのコマンドもpackage.jsonに登録。
npm install --save-dev nodemon
"scripts": {
"dev": "nodemon index.js"
}
これで、npm run dev コマンド一発で、サーバーが起動出来る様になる。
3、スターティングポイントのサーバーファイルとなる、index.js を作る
touch index.js
4、index.jsを開いて、サーバー起動のコードを書く
const express = require("express");
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send("Have an nice day :)");
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
5、コードを npm run dev で走らせる。
これで、http://localhost:3000/
に来た Get Requestに対して、シンプルに "Have an nice day :)" のメッセージを表示させる。
ほんま簡単だよね。
6、PostmanのVS Codeエクステンションをインストール
Postmanサイトでアカウントを作って、インストール
7、collection を作る
インストールが終わると、左にPostmanのヘルメットアイコンが出るので、それをクリック。左上の+アイコンを押して、適当な名前で新しいコレクションを作る。コレクションは、プロジェクト(アプリ)毎に作っておけば良い。整理目的。
8、PostmanからGet Request を送ってみる。
コードを少し書き直して、単純なHTMLをGETに応じて返してみる。
const express = require("express");
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send(`
<h1>Have a nice day :)</h1>
<p>What are you doing today?</p>
`);
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Postmanの画面
リクエストの種類(今回はGET)を選び、リクエストを送るURIを入れて、Sendをクリックすると、Responseのところに色々と結果が表示される。Bodyの部分に、受け取ったデータ、リクエストのステータスコード、リスポンスヘッダーやクッキーなど、ほぼ必要な情報は得られる様になっている。
上の部分は、送られたリクエストの情報である。ここで、Authの設定や、添付ファイルの追加、JSONデータの入力など、色々なリクエストをシュミレーションできる様になっている。凄いじゃろ! console.logにポチポチと出力していた時代とは、大違いや!
一つ一つのリクエストに名前を付けて保存しておけるので、いつでも同条件でのシュミレーションも可能である。
別に動的なAPIでなくても、普通にgoogle.comにPostmanからGet Requestを送ってみると、下の様にindexページのコードが送り返されていることがわかる。また、その際のヘッダーやクッキーも一目瞭然である。
どのサイトもHTTPで繋がっている以上、それならPostmanからもリクエストが送れるという事である。
勿論、GETだけではなく、HTTPの全てのリクエストに対応しているので、APIをテストする時に、Postmanを使う大きな意味があるのである。
9、JSON Serverのインストールとセットアップ
ということで、JSON Server ライブラリを使って、色々とRESTの処理をテストしてみる。
JSON ServerはREST APIのダミーを超簡単に作れるモジュールである。
いちいちデータベースの設定とか、面倒なことせんでも、JSONファイルを書くだけで、仮想データベースが作れる。
パッケージをインストール
npm install --save-dev json-server
ルーツ直下にdb.jsonという名前で、適当なjsonファイルを作る。
{
"posts": [
{ "id": 1, "title": "Postmanやばい", "article": "Postman知ってる?やばいっすよ" },
{ "id": 2, "title": "Postwoman", "article": "Postwoman襲来、Postman危うし" },
{ "id": 3, "title": "Postman 宇宙に帰る", "article": "Postmanのカラータイマーが点滅。宇宙に帰る" },
{ "id": 4, "title": "Rest API 現る", "article": "Rest エクステンションも気軽で良いよね。Postman涙目。" }
],
"auther": {
"name": "TakMoonWalker",
"country": "Nishinari"
}
}
package.jsonに、JSONサーバー起動コードを加える
"scripts": {
"dev": "nodemon index.js",
"json-server": "json-server --watch db.json"
}
あとは、
npm run json-server
を実行すれば、表示されてるエンドポイントからデータにアクセスできる様になる。 ほんま便利やな。
JSON Serverは、ポート番号3000で動いてるので、Nodeサーバーは3001に変更しました。
10、各エンドポイントを作る
まず、データをフェッチする為のaxiosをインストール
npm install express axios
各エンドポイントを作る
const jsonServerURL = 'http://localhost:3000/posts';
// Fetch all articles
app.get('/articles', async (req, res) => {
try {
const response = await axios.get(jsonServerURL);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Add new article
app.post('/articles', async (req, res) => {
try {
const newPost = req.body;
const response = await axios.post(jsonServerURL, newPost);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Update article
app.put('/articles/:id', async (req, res) => {
try {
const updatePost = req.body;
const { id } = req.params;
const response = await axios.put(`${jsonServerURL}/${id}`, updatePost);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Delete post
app.delete('/articles/:id', async (req, res) => {
try {
const { id } = req.params;
await axios.delete(`${jsonServerURL}/${id}`);
res.status(204).send();
} catch (error) {
res.status(500).json({ message: error.message });
}
});
こちらが、フルのコード
const express = require('express');
const app = express();
const axios = require('axios');
const port = 3001;
app.use(express.json()); // Middleware to parse JSON bodies
// return simple HTML when user accessed root
app.get('/', (req, res) => {
res.send(`
<h1>Have a nice day :)</h1>
<p>What are you doing today?</p>
`);
});
const jsonServerURL = 'http://localhost:3000/posts';
// Fetch all articles
app.get('/articles', async (req, res) => {
try {
const response = await axios.get(jsonServerURL);
console.log(response);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Add new article
app.post('/articles', async (req, res) => {
try {
const newPost = req.body;
const response = await axios.post(jsonServerURL, newPost);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Update article
app.put('/articles/:id', async (req, res) => {
try {
const updatePost = req.body;
const { id } = req.params;
const response = await axios.put(`${jsonServerURL}/${id}`, updatePost);
res.json(response.data);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Delete post
app.delete('/articles/:id', async (req, res) => {
try {
const { id } = req.params;
await axios.delete(`${jsonServerURL}/${id}`);
res.status(204).send();
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
で、これらのコードがちゃんと動いているのか、フロント側を作っていない場合、どうやって確認したらええねん という事になるので、そこで、お兄さん、Postmanありますやん。
上から順番にテストしてみると
1、/articles にGet requestを送ると、全ての記事が返ってくる。
2、/articles に 新しい記事をbodyに入れて、Post requestを送ると、新しい記事が追加される。
試しに、1の記事一覧をフェッチするリクエストを送ると、完璧に駄文が追加されている。
3、/articles/:id に、更新した記事をPUTリクエストで送ると、記事がアップデータされる。
お分かりであろうか?先ほどの駄文が、さらに駄文にアップデートされたのが。
4、最後に、/articles/:id にDELETEリクエストを送り、駄文を消去。
DELETE Requestを送った後に、記事一覧をフェッチ。ID:5 の駄文が消えているのがお分かりであろうか?
かなり長くなってしまったが、PostmanはヘッダーにTokenを入れる事で、ログイン、ログアウト、サインアップなどのサーバー側動作のシュミレーションなども出来るし、フリーのアプリなのに、出来ることが盛り沢山すぎて、全部書いていたら巻物レベルの長さになり、誰も最後まで読まないだろうと思うのですが、Qiitaというブログベースのサービスである以上、文字で書かんとあかんと思い、ここまで頑張って書いてきたが、こういうチュートリアルは動画の方がサクッと見れて、便利ですよ。
まとめ:サーバーサイドの動きや、APIがちゃんと動いているのかテストしたい時は、Postmanがいるのといないのとでは、全裸で外を歩くのか、最低限パンツとソックスを身につけて歩のかくらいの安心感の違いがありました。