読者です 読者をやめる 読者になる 読者になる

いまさらながら Express 入門

Express.js

こいつを調べようと思って、

LoopBack - Node.js framework

Express ベースらしいので、最初はそっちから戯れる。

Hello Express!

$  mkdir hello_express
$  cd hello_express
$  npm init
$  npm install express --save
$ cat app.js 
const express = require('express')
const app = express()

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

app.listen(3000, () => {
  console.log('Example app listening on port 3000!')
})

reqres は Node 標準 API の HTTP のやつと同じ。

$ node app.js
$ curl http://localhost:3000/

Routing 基本

基本形。

app.METHOD(PATH, HANDLER)

GETとPOSTのルーティング。

app.get('/', (req, res) => {
  res.send('Hello World!')
})
app.post('/', (req, res) => {
  res.send('Got a POST request')
})

Static ファイルの配信

ビルドインの express.static middleware を使う。

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

特定URLでマッピングするなら、 app.use() の第1引数にパスを指定する。

app.use('/static', express.static('public'))

ファイルの探索はプログラム起動したディレクトリからの相対パスなので、絶対パスで指定するのがよい。

app.use('/static', express.static(path.join(__dirname, 'public')))

404 のハンドリング方法

全ての middleware と route を実行した結果、どれもレスポンスを返せなかった場合に 404 となる。なので一番最後に以下 middleware を定義すればよい。

app.use((req, res, next) => {
  res.status(404).send("Sorry can't find that!")
})

エラーハンドリング

引数4つの middleware でエラーハンドリングとなる。

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

Routing

この基本形。

app.METHOD(PATH, HANDLER)

METHOD は HTTP メソッドと対応してて、めっちゃ定義されている。

get, post, put, head, delete, options, trace, copy, lock, mkcol,  move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search, and connect

app.all() という HTTP メソッドと対応していないやつがあり、HTTP メソッド関係ない。特定パスに対してのみ middleware を使うのに等しいのかな。

app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section ...')
  next() // pass control to the next handler
})

PATH にはパターンや正規表現が使える。 ?, +, *, (, ) はパターンで処理される。

app.get('/ab?cd', (req, res) => {}) // `/abcd`, `/acd`
app.get('/ab+cd', (req, res) => {}) // `/abcd`, `/abbcd`, `/abbbcd`
app.get('/ab*cd', (req, res) => {}) // `/abcd`, `/abxcd`, `/abRANDOMcd`, `/ab123cd`
app.get('/ab(cd)?e', (req, res) => {}) // `/abe`, `/abcde`
app.get('/ab(cd)?e', (req, res) => {}) // `/abe`, `/abcde`
app.get(/a/, (req, res) => {}) // `a` 含むもの全て

: でURLの部分文字列がパラメータにマッピングされる。

app.get('/users/:userId/books/:bookId', (req, res) => {
  console.log(req.params) // /users/1/books/23:title] => { userId: '1', bookId: '23' }
})

-, . はその文字のまま取り扱われる。なので、以下定義ではそれぞれ from, to および genus, species がキャプチャ対象。

/flights/:from-:to
/plantae/:genus.:species

HANDLER は複数指定できる。next() を呼ばないと次のハンドラには行かない。

app.get('/example/b', (req, res, next) => {
  console.log('the response will be sent by the next function ...')
  next()
}, (req, res) => {
  res.send('Hello from B!')
})

Arrayでもよい。

app.get('/example/c', [cb0, cb1, cb2])

express.Router というやつを使うと、いくつかの route や middleware 利用をまとめてモジュールとできる。

var express = require('express')
var router = express.Router()

// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
  console.log('Time: ', Date.now())
  next()
})
// define the home page route
router.get('/', function (req, res) {
  res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
  res.send('About birds')
})

module.exports = router
var birds = require('./birds')
app.use('/birds', birds)

middleware と route の実行順

定義した順。next() を呼ぶ限り、次の middleware or route が実行される。

next 仮引数

次の middleware 関数ってことらしい。

next('route') とすると、次の middleware へ飛ぶ(stack を辿らない)。以下は 1-12 が出力される。

app.get('/', [
  (req, res, next) => {
    console.log('1-1')
    next('route')
  },
  (req, res) => {
    console.log('1-2')
  }
])
app.get('/', (req, res) => {
  console.log('2')
})

テンプレートエンジン

テンプレートエンジンは I/F が準拠してるものは全て使える。

  • app.set('views', './views') でテンプレートファイルがあるディレクトリを指定する
  • app.set('view engine', 'pug') でテンプレートエンジンを指定する

上記が設定されていれば、 res.render() を呼び出しすればよい。

res.render('index', { title: 'Hey', message: 'Hello there!' })

エラーハンドラする middleware を複数

app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

logErrors では next(err) として、次のエラーハンドラに err オブジェクトを渡してあげる必要がある。つまり next() に引数を渡すと次のエラーハンドラが実行されるってことらしい。

function logErrors (err, req, res, next) {
  console.error(err.stack)
  next(err)
}

デバッグ

環境変数で出力有無を制御できる。

$ DEBUG=express:* node index.js

値の指定方法は debug モジュールってやつを利用してて、名前空間的なアプローチっぽい。

app.set()

設定値的なやつを設定すると middleware をまたいで値の共有ができる。