Expressのメモ

### app.get()を使ったAPIの作成

```JavaScript:main.js
import express from "express";

const PORT = 8080;
const app = express(); // 意味合いとしてhttp.createServerで記述した時のserverがappに当たる（慣習としてappと命名する事が多い）

app.use(express.urlencoded({ extended: true })); // これを記述しないとPOST時のbodyが正常に受け取れない

// expressではgetメソッド, パスのURL, コールバック関数 を↓のように指定する
app.get("/", function (req, res) {
  /* [プレーンなnodeとの違い]
    whiteメソッド, endメソッドは記述しないで sendメソッド に処理をまとめる
    sendは1つのブロック内で複数呼び出せない
  */
  res.send(`
  <a href="/result?hoge=fizz&fuga=buzz">GET method link</a>
  <form action="/result" method="post">
    <input type="text" name="title" placeholder="title"><br />
    <input type="text" name="description" placeholder="description"><br />
    <input type="text" name="arr[]" placeholder="array1"><br />
    <input type="text" name="arr[]" placeholder="array2"><br />
    <input type="submit">
  </form>
  `);
});

// /result?hoge=fizz&fuga=buzz にアクセスがあった時に、クエリパラメータを取得する処理
app.get("/result", function (req, res) {
  const queryParams = req.query;
  console.log(queryParams); // -> { hoge: 'fizz', fuga: 'buzz' }
});

// /result に対してPOSTリクエストを受け付けたときの処理 ※app.use(express.urlencoded())を記述しないと動作しないので注意
app.post("/result", function (req, res) {
  const body = req.body;
  console.log(body); // -> { title: '入力した値', description: '入力した値', arr: [ '入力した値', '入力した値' ] }
});

/*
  サーバーを起動するためにlistenメソッドを使うのはhttp.createServerと同じ
  コールバック関数に起動後の処理を記述できる（http.createServerも同様にコールバック関数に処理を記述することができる）
*/
app.listen(PORT, function () {
  console.log(`server start! post is ${PORT}`);
});
```

### Routerを使ったAPIの作成

```JavaScript:router.js
/*
  expressの機能の1つであるRouterを使用する
  従来app.get(...)で記述していたのをrouter.get(...)の記述にする
  このモジュールで記述した処理を外部で読み込ませて使用する
*/

import express from "express";
const router = express.Router();

const todos = [
  { id: 1, text: "todoA" },
  { id: 2, text: "todoB" },
  { id: 3, text: "todoC" },
];

// /todosに対してGETリクエストを受けたときにJSON形式でレスポンスを返す処理
router.get("/todos", function (req, res) {
  res.json(todos);
});

export default router;
```

```JavaScript:main.js
import express from "express";
import router from "./router.js";

const PORT = 8080;
const app = express();

app.use(express.json());

/*
  Routerを使った処理を記述している外部モジュールを読み込み際はapp.use(...)を使う
  app.useの第一引数にパスを指定することができる
*/
app.use("/toods", router);

app.listen(PORT, () => {
  console.log(`server start! post is ${PORT}`);
});
```

### RouteHandler（ルートハンドラ）とは
- `app.get('/', function(req, res) {})` ←のコードにおける `.get` `.post` `.put` `.patch` `.delete` などの関数のこと
- **APIのレスポンスを記述する処理**はRouteHandlerで行う
- .get()の第一引数に指定するパスは、**リクエストとして取得したパスと完全一致**したケースで処理を行う

### Middleware（ミドルウェア） とは
- ルートハンドラの前後に行われる処理のこと
- 一般的には `app.use('/', function(req, res, next) {})` ←のように `.use()` を使って記述する処理のこと
- **リクエストを受けたときにRouteHandlerで行う処理の前に実行したい処理**はMiddlewareで行う
- `app.use(express.json());` のような処理はMiddlewareに記述する
- .use()の第一引数に指定するパスは、**リクエストとして取得したパスと前方一致**したケースで処理を行う

#### Middlewareのnext()とは
- Middlewareの中で次の処理へ進む際に必要なメソッドのこと
- `next()` を記述すると、次のMiddlewareの処理を実行することができる

#### Middlewareの実装例

条件分岐で該当するケースの場合、nextをコールして次の処理を実行して関数の処理を終了させる
```JavaScript
app.use('/', function(req, res, next) {
  if (true) {
    return next()
    // next()の処理を実行してreturnさせる処理で ↓ と同じ意味となる（returnされているため、console.log('hoge')は呼ばれない）
    next()
    return
  }
  console.log('hoge')
})
```

例外処理を記述したいケースでは、next()の引数に値を渡す
```JavaScript
app.use('/', function(req, res, next) {
  if (true) {
    return next('エラーが発生しました') // エラーが発生していると認識されるため、後続の処理は実行されない
  }
})
```

### エラーハンドラについて
- 例外が発生したときに動作する挙動を記述するもの
- `app.use(function(error, req, res, next) {})` のように、引数を4つ（第一引数にはerror）指定するとエラーハンドラとして機能する
- `res.headersSent` の判定式を使ってレスポンスの重複によるエラーを回避してあげる必要がある
```JavaScript
app.use('/', function(req, res, next) {
  if (true) {
    return next('エラーが発生しました') // next()の引数に何かしらの値がある場合はエラーとして認識されて、エラーハンドラが動く
  }
})

app.use(function(error, req, res, next) {
  // next()の引数の値がerrorに渡ってくる

  /*
    すでにレスポンスが返却されているのに更にレスポンスを返却してしまうと、レスポンスの重複によるエラーをになってしまうため、それを防ぎたい
    res.headersSentは、すでにレスポンスがブラウザ側に返却されている場合はtrueになる
    エラーハンドル内でのnext()の挙動は、expressのデフォルトのエラーハンドラが呼ばれることになる
  */
  if (res.headersSent) { 
    return next(error) // expressのデフォルトのエラーハンドラの処理を行い処理を終了することができ、レスポンスの重複によるエラーを回避することができる
  }
  req.json({ error: error }) // -> { "error": "エラーが発生しました" }
})
```