Express.js完全入門

# Express.jsとは

Express.jsは、Node.jsのための軽量で柔軟なWebアプリケーションフレームワークです。
Express.jsを使用すると、Node.jsを使用してWebアプリケーションを作成するための様々な機能を簡単に実装できます。

MVCアーキテクチャを採用し、ルーティング、テンプレートエンジン、ミドルウェア、セッション、Cookieの処理、静的ファイルの提供、データベースの操作など、Webアプリケーション開発に必要な基本的な機能を提供しています。
また、豊富なサードパーティーモジュールを使用することで、より高度な機能を追加することも可能です。

# Express.jsの使い方

## 事前準備
Node.jsをインストールし、npmを使用できる環境を作っておく必要があります。
- [【Windows/Mac/Linux（Ubuntu）】Node.jsインストール方法](https://qiita.com/ryome/items/eec08b28aff294e8c3d6)

```shell:確認コマンド
>node -v
v14.16.0

>npm -v
6.14.11
```

## 環境構築
**1. プロジェクトの作成**

新しいディレクトリを作成し、そのディレクトリでnpmプロジェクトを初期化します。

```shell:プロジェクト作成コマンド
>mkdir my-express-project

>cd my-express-project

>npm init --yes
Wrote to C:\【path】\my-express-project\package.json:

{
  "name": "my-express-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

**2. Express.jsのインストール**

npmを使用して、以下のコマンドを実行してExpress.jsをインストールします。

```shell:Express.jsインストールコマンド
>npm install express

+ express@4.18.2
added 57 packages from 42 contributors and audited 57 packages in 2.353s

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```

**3. サーバーの作成**

`index.js` ファイルを作成して、以下コードを作成します。

```js:index.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});
```

`package.json` にstartコマンドを追加します。

```json:package.json
{
  "name": "my-express-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2"
  }
}
```

## 動作確認

**1. サーバ起動**

以下のコマンドを実行してサーバを起動します。

```shell:サーバ起動コマンド
>npm start

> my-express-project@1.0.0 start C:\【path】\my-express-project
> node index.js

Server listening on port 3000
```

**2. サーバ確認**

`curl` コマンドを使用してサーバの実行を確認します。

```shell:curlコマンド
>curl http://localhost:3000
Hello World!
```

**3. サーバ停止**

`ctrl + c` でサーバを停止できます。
（Macでは `control + c`）

## ルーティング

Express.jsにおけるルーティングとは、HTTPリクエストのURLに応じて、アプリケーション内で実行する処理を決定する仕組みです。ルーティングは、HTTPメソッド（GET、POST、PUT、DELETEなど）とURLパターンによって定義されます。

Express.jsでは、`app.【HTTPメソッド名】()` 関数を使用してルートを定義します。
`app.get()`、`app.post()`、`app.put()`、`app.delete()` などのHTTPメソッドに対応した関数を使用することができます。

以下は、GETリクエストのルートを定義する例です。

```js:GETリクエストのルート定義例
app.get('/', function (req, res) {
  res.send('Hello World!')
})
```

上記の例では、ルートに対するGETリクエストを定義しています。
ルートに対するリクエストが発生した場合、第2引数に指定されたコールバック関数が呼び出されます。
この関数内で、レスポンスを返すための処理を行います。
各コールバック関数の第1引数 `req` はリクエストオブジェクト、第2引数 `res` はレスポンスオブジェクトです。

また、URLパラメータを使用して、動的なルートを定義することもできます。

```js:動的なルート定義例
app.get('/users/:id', function (req, res) {
  res.send('User ID: ' + req.params.id)
})
```

この例では、`/users/:id` というパスを定義しています。
`:id` はURLパラメータで、リクエストURLに含まれる任意の文字列にマッチします。
このURLパラメータは、`req.params` オブジェクトを介して取得することができます。

また、複数のハンドラ関数を使用して、ルートに対する処理を定義することもできます。

```js:複数のハンドラ関数を使用したルート定義例
app.get('/users/:id', function (req, res, next) {
  // ミドルウェアの処理
  next()
}, function (req, res) {
  res.send('User ID: ' + req.params.id)
})
```

この例では、2つのハンドラ関数を使用しています。
最初のハンドラ関数は、リクエストに対するミドルウェアの処理を行います。
このミドルウェアの処理が完了した後、次のハンドラ関数が実行され、レスポンスが返されます。

Express.jsのルーティングを用いてREST APIを実装することができます。

**REST APIとは**

REST (Representational State Transfer) APIは、Webサービスを開発するためのアーキテクチャスタイルの1つで、HTTPプロトコルを使用してWeb上でのデータのやりとりを行うための方法論です。

REST APIでは、リソースを一意の識別子（URI）によって表現し、HTTPメソッド（GET、POST、PUT、DELETEなど）を使用してリソースに対して操作を行います。

| HTTPメソッド | 操作 | 
| --- | --- | 
| GET | リソースを取得する | 
| POST | リソースを作成する | 
| PUT | 指定したIDのリソースを更新する | 
| DELETE | 指定したIDのリソースを削除する | 

**REST API実装**

`index.js` を以下のように修正すると、 `GET`, `POST`, `PUT`, `DELETE` のHTTPメソッドを使用してリクエストを処理するようにできます。

```js:index.js
const express = require('express');
const app = express();

// GETリクエスト
app.get('/', (req, res) => {
  res.send({
    msg:'GET request'
  });
});

// POSTリクエスト
app.post('/', (req, res) => {
  res.send({
    msg:'POST request'
  });
});

// PUTリクエスト
app.put('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'PUT request'
  });
});

// DELETEリクエスト
app.delete('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'DELETE request'
  });
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
```

`curl` コマンドを使用してサーバの実行を確認できます。

```shell:curlコマンド
>curl -X GET http://localhost:3000/
{"msg":"GET request"}
>curl -X POST http://localhost:3000/
{"msg":"POST request"}        
>curl -X PUT http://localhost:3000/123
{"id":"123","msg":"PUT request"}
>curl -X DELETE http://localhost:3000/123
{"id":"123","msg":"DELETE request"}
```

## ミドルウェア

Express.jsにおけるミドルウェアとは、HTTPリクエストとレスポンスの間に挟まる処理を指します。
ミドルウェアは、アプリケーション全体で共通の処理や、特定のルートで実行する必要がある処理などに使用されます。

Express.jsでは、`app.use()` 関数を使用してミドルウェアを登録します。
登録されたミドルウェアは、HTTPリクエストが発生するたびに呼び出されます。
ミドルウェアの実装方法は、以下のようになります。

```js:ミドルウェア実装
// ミドルウェアの実装
const myMiddleware = function(req, res, next) {
  // ミドルウェアの処理
  next(); // 次のミドルウェアへ処理を移す
};

// ミドルウェアの登録
app.use(myMiddleware);
```

このように、`app.use()` 関数に登録するミドルウェア関数には、`req` 、`res` 、`next` という3つの引数が渡されます。
`req` はリクエストオブジェクト、`res` はレスポンスオブジェクト、`next` は次のミドルウェア関数へ処理を移すための関数です。

**ミドルウェア実装**

`index.js` を以下のように修正すると、 HTTPリクエストのメソッドとURLをログに出力するようにできます。

```js:index.js
const express = require('express');
const app = express();

const loggerMiddleware = function(req, res, next) {
  console.log(`[${new Date()}] ${req.method} ${req.url}`);
  next();
};

app.use(loggerMiddleware);

// GETリクエスト
app.get('/', (req, res) => {
  res.send({
    msg:'GET request'
  });
});

// POSTリクエスト
app.post('/', (req, res) => {
  res.send({
    msg:'POST request'
  });
});

// PUTリクエスト
app.put('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'PUT request'
  });
});

// DELETEリクエスト
app.delete('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'DELETE request'
  });
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
```

`curl` コマンドを使用してサーバの実行を確認できます。

```shell:curlコマンド
>curl -X GET http://localhost:3000/
{"msg":"GET request"}
>curl -X POST http://localhost:3000/
{"msg":"POST request"}        
>curl -X PUT http://localhost:3000/123
{"id":"123","msg":"PUT request"}
>curl -X DELETE http://localhost:3000/123
{"id":"123","msg":"DELETE request"}
```

上記コマンド実行時にサーバ側のコンソールで以下のログが表示されます。

```shell:コンソール
[Sun Mar 19 2023 01:42:24 GMT+0900 (日本標準時)] GET /
[Sun Mar 19 2023 01:42:29 GMT+0900 (日本標準時)] POST /
[Sun Mar 19 2023 01:42:34 GMT+0900 (日本標準時)] PUT /123
[Sun Mar 19 2023 01:42:39 GMT+0900 (日本標準時)] DELETE /123
```

## 静的ファイル

Webアプリケーションでは、静的ファイル（画像、CSS、JavaScriptなど）を扱うことが一般的です。

以下では、Express.jsで静的ファイルを扱う方法について解説します。

**静的ファイルの配置場所を指定する**

まず、静的ファイルが配置されている場所を指定する必要があります。
Express.jsでは、`express.static` ミドルウェアを使用して、静的ファイルのディレクトリを指定できます。

例えば、以下のように記述することで、publicディレクトリ内の静的ファイルを使用できます。

```js
app.use(express.static('public'))
```

**ルーティングの設定による静的ファイルの提供**

ルーティングを設定して、public/imagesディレクトリ内のファイルを提供しています。

`options`オブジェクトには、ファイル送信時の設定が含まれています。

`fileName`には、リクエストパラメータのファイル名が格納されます。

```js
app.get('/images/:filename', function(req, res) {
  var options = {
    root: __dirname + '/public/images/',
    dotfiles: 'deny',
    headers: {
      'x-timestamp': Date.now(),
      'x-sent': true
    }
  };

  var fileName = req.params.filename;
  res.sendFile(fileName, options, function (err) {
    if (err) {
      next(err);
    } else {
      console.log('Sent:', fileName);
    }
  });
});
```

この例では、/images/:filenameというパスに対して、public/imagesディレクトリ内のファイルを提供するルーティングを設定しています。

`options`オブジェクトには、ファイル送信時の設定が含まれています。

`root`には、ファイルのルートディレクトリを指定します。

`dotfiles`には、ドットで始まるファイルの扱い方を指定します。

この例では、denyを指定して、ドットで始まるファイルを送信しないようにしています。

`headers`には、レスポンスヘッダーを指定します。この例では、x-timestampとx-sentを指定しています。

`fileName`には、リクエストパラメータのファイル名が格納されます。`res.sendFile`メソッドを使用して、ファイルを送信します。

送信が完了したら、コールバック関数が実行されます。エラーが発生した場合は、next関数にエラーを渡します。


**静的ファイルのキャッシュ**

Express.jsでは、静的ファイルのキャッシュ機能を提供しています。キャッシュを使用することで、同じファイルに対して複数のリクエストがあった場合でも、サーバーからのレスポンス時間を短縮することができます。

キャッシュを有効化するには、`maxAge`オプションを使用します。以下は、キャッシュを有効化する例です。

```js
app.use(express.static('public', { maxAge: 86400000 }));
```

`maxAge`には、キャッシュの有効期限をミリ秒単位で指定します。上記の例では、1日間キャッシュを有効化しています。

キャッシュを有効化することで、同じファイルに対して複数回のリクエストがあった場合でも、サーバーからのレスポンス時間を短縮することができます。

ただし、キャッシュ期間が長すぎると、ファイルの変更が反映されない場合があるので、適切な期間を設定する必要があります。

**静的ファイルの実装**

`index.js` を以下のように修正すると、 `/test-image` パスにアクセスして `public/images/test.png` が返却するようにできます。

```js:index.js
const express = require('express');
const app = express();
const path = require('path');

const loggerMiddleware = function(req, res, next) {
  console.log(`[${new Date()}] ${req.method} ${req.url}`);
  next();
};

app.use(loggerMiddleware);

// 静的ファイルの提供とキャッシュの有効化
app.use(express.static('public', { maxAge: 86400000 }));

// test-imageリクエスト
app.get('/test-image', (req, res) => {
  res.sendFile(path.join(__dirname, 'public/images/test.png'));
});

// GETリクエスト
app.get('/', (req, res) => {
  res.send({
    msg:'GET request'
  });
});

// POSTリクエスト
app.post('/', (req, res) => {
  res.send({
    msg:'POST request'
  });
});

// PUTリクエスト
app.put('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'PUT request'
  });
});

// DELETEリクエスト
app.delete('/:id', (req, res) => {
  res.send({
    id: req.params.id,
    msg:'DELETE request'
  });
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
```

このコードでは、`/test-image`パスにアクセスすると`public/images/test.png`が返却されます。

`path.join(__dirname, 'public/images/test.png')`を使用して、現在のディレクトリからの相対パスでファイルを指定しています。

[localhost:3000/test-image](http://localhost:3000/test-image)にアクセスしてサーバの実行を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/f3e03f3e-9f63-49bb-bc06-ca1b5ef56f49.png)

# GitHub

今回の記事で紹介したソースコードをGitHubで公開しています。

https://github.com/ryomeblog/express-tmp