[Node.js] expressで作るHTTPサーバ - 入門編 その1

※この記事は専門学校の講義用に作成されたものです

Node.jsでHTTPサーバを作成します。

以前からNode.jsはサーバを作るための言語であるという誤解している方が結構いらっしゃるのですが、標準モジュールだけでも非常に気軽にHTTPサーバをポンッと作成して立ち上げることが出来てしまうことも起因しているのだと思います。外部のモジュールを取ってくれば様々なサーバ・ソフトウェアが転がっていることでも更に拍車をかけたのでしょう。

今回は標準のモジュールに加えてExpressと呼ばれるモジュールと組み合わせることで、さらに簡単にWebサーバを構築する手順について解説します。

環境準備

nodeとnpmをインストール

Node.jsをインストールします。LTS版でも最新版でもどちらでもかまいません。 https://nodejs.org/ja/nodejs.org

Node.jsをインストールすると自動的にnpmも入ります。npmは「Node Package Manager」の略称です。Web上に公開されている数多くのモジュールをインストールしたり、自分自身がNode.jsで開発を行う際に様々な便利な機能を提供してくれます。

Gitのリポジトリを作成

GitHubなどに新しくリポジトリを作成します。

ここではmyserverという名前で作成しました。

リポジトリ作成完了画面の特にgit remote add (略)の部分をメモしておきます。

Node.js用のプロジェクトを作成

フォルダの作成

プロジェクトと言うと大げさに聞こえますが、各種ファイルを保存しておく単なるフォルダです。

$ mkdir myserver
  • Windowsの場合は右クリックなどでフォルダを作成すればOK

Git

作成したフォルダをGit用に初期化しリモートの登録を行います。

$ cd myserver

$ git init
Initialized empty Git repository in /Users/katsube/Desktop/myserver/.git/

$ git remote add origin https://github.com/katsube/myserver.git

また.gitignoreという名前で次の内容のファイルを作成します。このファイルに記載したファイルやディレクトリはGitに登録されません。node_modulesはnpmでインストールする外部のモジュールのファイルが記録されますが、インストールすれば同じものが取得できるためGitには通常登録しません。

$ cat .gitignore
node_modules/

package.jsonの作成

プロジェクトの様々な情報を記述する「package.json」というファイルを作るのですが、これはnpmで対話的に作成することができます。

$ npm init
package name: (myserver) 
version: (1.0.0) 
description: My 1st Web Server 
entry point: (index.js) 
test command: 
git repository: (https://github.com/katsube/myserver.git) 
keywords: 
author: M.Katsube
license: (ISC) 

色々聞かれますが、各項目のざっくりとした意味は以下の通りです。

項目 説明
package name ディレクトリ名がデフォで入ります。お好きな英数字の文字列を
version 完成品を最初から上げない場合は 0.0.1 や 0.1.0などを入れます。
description 説明文。省略可能です。
entry point 一番最初に実行されるファイルを指定します。
test command 省略可能
git repository Gitのリポジトリが自動で入ります。別途指定したい場合は入力を
keywords 検索用のキーワード。省略可。複数ある場合はカンマで区切ります
license ライセンスを指定します。こちらのページのIdentifier列の文字列を入力します

今回は以下のような感じで出来ました。

$ cat package.json
{
  "name": "myserver",
  "version": "1.0.0",
  "description": "My 1st Web Server ",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/katsube/myserver.git"
  },
  "author": "M.Katsube",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/katsube/myserver/issues"
  },
  "homepage": "https://github.com/katsube/myserver#readme"
}

git commit/push

ここまでをGitでcommit, pushしておきます。

$ git add .
$ git commit -m '1st commit'
$ git push

ここまではNode.js用の開発を行う際には毎度やることになります。

expressでHTTPサーバを作成する

expressをインストール

外部のモジュールをインストールするには、npmコマンドで一発です。モジュールが必要としている他のモジュールも自動的にインストールされます。

$ npm install express

インストールしたファイルはnode_modulesフォルダの下にすべて保存されます。試しに覗いてみると思わず「うぉ」と声が出そうになりますw いやー、大量ですね。

ここでpackage.jsonを開くと、下の方にdependenciesという項目が増え、先ほどインストールしたモジュールの名前とバージョンが書かれています。このようにpackage.jsonはプロジェクトが依存しているモジュールが記録されます。この情報を元に他の人が全く同じ環境を構築することができます。

$ cat package.json
{
  (中略)
  "dependencies": {
    "express": "^4.17.1"
  }
}

またpackage-lock.jsonという新しいファイルも作成されています。これはpackage.jsonだけでは足りない各種モジュールのバージョン情報などが記録されています。

ソースコード

早速HelloWorldから行きましょう。以下のコードをserve.jsという名前で保存します。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  res.send('Hello World!');
  console.log("/ へアクセスがありました");
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});

実行する

Nodeコマンドに先ほど保存したファイルのパスを指定し実行します。以下のメッセージが表示され、ずっと終了せず起動しっぱなしになれば成功です。

$ node serve.js
listening at http://localhost:3000

ブラウザからhttp://localhost:3000へアクセスしてみましょう。無事にメッセージが表示されたでしょうか?

Terminalにも変化があります。以下のようにconsole.log()をするとブラウザに返さずにTerminal上に情報を表示することができます。

終了する

このままだと永遠に起動し続けますので、終了した場合にはCtrl+cで強制的にプログラムの実行を終了します。この状態でもう一度ブラウザからhttp://localhost:3000へアクセスし、接続できなければサーバが無事に終了しています。

$ node serve.js
listening at http://localhost:3000
^C

expressの各種機能

ファイルを返却する

変更したのは7行目です。res.send()はブラウザに文字列を直接返却しますが、res.sendFile()はその名の通り指定したファイルを返却します。__dirnameは現在のディレクトリのパスを示す特別な変数です。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  res.sendFile(`${__dirname}/public/index.html`);
  console.log("/ へアクセスがありました");
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});

publicフォルダを作成し、以下のような簡単なHTMLをindex.htmlという名前で保存します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf8">
  <title>Example</title>
</head>
<body>

<h1>Hello! Node.js</h1>
<p>I am public/index.html</p>

</body>
</html>

ブラウザで次のように表示されたでしょうか?

パラメーター

例えば3つのファイルがありそれぞれを別のURLで返却したい場合、次のように書いても動作しますが、例えばファイルが100個や1000個になってくると生き地獄ですw

app.get("/a", (req, res) =>{
  res.sendFile(`${__dirname}/public/a.html`);
});
app.get("/b", (req, res) =>{
  res.sendFile(`${__dirname}/public/b.html`);
});
app.get("/c", (req, res) =>{
  res.sendFile(`${__dirname}/public/c.html`);
});

そんなときには次のようにパラメーター機能を利用します。追加したのは10〜15行目です。ポイントは/image/:file:fileの部分です。先頭にコロン(:)を付けた文字列は変数のような扱いになります。つまり/image/以降にどのような文字列が来てもマッチします。マッチした文字列はreq.params.(パラメーター名)で取得することができます。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  res.sendFile(`${__dirname}/public/index.html`);
  console.log("/ へアクセスがありました");
});
app.get("/image/:file", (req, res) =>{
  const file = req.params.file;

  res.sendFile(`${__dirname}/public/image/${file}`);
  console.log(`/image/${file} へアクセスがありました`);
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});

publicフォルダの下にimageフォルダを新しく追加し適当な画像をアップロードします。

bear.png cat.png usa.png

画像を呼び出すHTMLを以下のように適当に用意します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf8">
  <title>Example</title>
  <style>ul{list-style-type:none; display:flex;}</style>
</head>
<body>

<h1>Hello! Node.js</h1>
<p>I am public/index.html</p>

<ul>
  <li><img src="/image/usa.png"  width="150"></li>
  <li><img src="/image/cat.png"  width="150"></li>
  <li><img src="/image/bear.png" width="150"></li>
</ul>

</body>
</html>

ブラウザでアクセスすると無事に表示できたでしょうか?

Terminal上で合計4回のアクセス(リクエスト)が発生していることがわかりますね。

$ node serve.js
listening at http://localhost:3000
/ へアクセスがありました
/image/usa.png へアクセスがありました
/image/cat.png へアクセスがありました
/image/bear.png へアクセスがありました

クエリーを受け取る

ブラウザから引数を受け取ることももちろん可能です。

GET

ポイント7行目です。この場合はhttp://localhost:3000/?name=TAKIといった感じでアクセスすると、URLのお尻に付けたクエリーがreq.query.(クエリー名)といった感じで取得できます。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  const name = req.query.name;
  res.send(`君の名は <strong>${name}</strong>`);
  console.log("/ へアクセスがありました");
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});

POST

POSTの場合はちょっとだけ手がかかります。

コードはこちら。

const express = require("express");
const app  = express();
const port = 3000;

// POSTのクエリーを良い感じに処理する
app.use(express.urlencoded({extended: true}));

// ルーティングの設定
app.post("/", (req, res) =>{
  const name = req.body.name;
  res.send(`君の名は <strong>${name}</strong>`);
  console.log("/ へアクセスがありました");
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});
  • 6行目でPOSTで渡されたデータを簡単に受け取れるような設定をします
  • 9行目の部分はこれまでapp.get()でしたがここがapp.post()になっています。ここに書かれていたのはGETやPOSTなどのメソッドのことを指しています。今回はPOSTで受け取りますのでapp.post()を利用します。
  • 10行目でPOSTで渡されたクエリーを受け取っています。req.queryではなくreq.bodyになっている点にも注意が必要です。

動作確認するには以下のようなHTMLを書くと良いでしょう。

<form action="http://localhost:3000/" method="POST">
  <input type="text" name="name">
  <button>送信</button>
</form>

実は少し前までは別のモジュールをインストールする必要がありましたが、Express v4.16.0以降では今回紹介したように最初から入っている機能で対応可能になりました。 www.npmjs.com

JSONを返却する

res.json()というメソッドが用意されています。これに連想配列を渡すだけです。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  const data = {
    "message": "Hello world",
    "date": "2020-06-29"
  };

  res.json(data);
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});
  • 7〜10行目でデータを用意します。
  • 12行目でJSON形式に変換したデータをブラウザに返します。

実行するとご覧の通り(Chromeの機能拡張で整形して表示しています)

続き

blog.katsubemakito.net

参考ページ