4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Expressでのエラーハンドリングでハマった

Posted at

Express触ってたら例外処理でハマったのでメモを残しておきます。そんなに大した理由ではありませんでした。

先に結論

ドキュメントをちゃんと読む

前提

Node.js 14.15.3
Express 4.17.1
Mongoose 5.11.9
express-async-errors 3.1.1

起こったこと

所謂MERN stackを学習中なのですが、ユーザーを作成してDBに保存するAPIを作成していました。こんな感じのよくチュートリアルにあるようなやつです

./controllers/users.js
const usersRouter = require('express').Router()
const User = require('../models/user')
const bcrypt = require('bcrypt')

usersRouter.get('/', async (request, response) => {
	const users = await User.find({})
	response.json(users)
})

/**
 * create new user
 */
usersRouter.post('/', async (request, response) => {
	const body = request.body

	const saltRounds = 10
	const passwordHash = await bcrypt.hash(body.password, saltRounds)

	const user = new User({
		username: body.username,
		name: body.name,
		password: passwordHash
	})

	const savedUser = await user.save()

	response.json(savedUser)
})

module.exports = usersRouter

で、usernameのユニークバリデーションを確認するためにちょっとPostmanでPOSTリクエスト送ってみたのですが、全然レスポンスが返ってきませんでした。
確認すると以下のようなエラーが。

(node:32647) UnhandledPromiseRejectionWarning: ValidationError: User validation failed: username: Error, expected `username` to be unique. Value: `ichiro`
    at model.Document.invalidate (/Users/xxx/projects/bloglist/node_modules/mongoose/lib/document.js:2693:32)
    at /Users/xxx/projects/bloglist/node_modules/mongoose/lib/document.js:2513:17
    at /Users/xxx/projects/bloglist/node_modules/mongoose/lib/schematype.js:1241:9
    at processTicksAndRejections (internal/process/task_queues.js:75:11)
(node:32647) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:32647) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

try catchしろよという話なのですが問題はそこではなくて、express-async-errorsをインストールしていたのにこのエラーが出たという点です。

express-async-errorはExpressでasync/awaitを使用する際にtry catchを書かなくてもよしなにやってくれるようなライブラリです。
https://www.npmjs.com/package/express-async-errors

意図していた挙動

以下のようなエラーハンドリング用のミドルウェアを定義していたので、バリデーションエラーの場合はステータスコード400でエラーメッセージが返ってくる想定でした。

./utils/middleware.js
const errorHandler = (error, request, response, next) => {
	if (error.name === 'ValidationError') {
		return response.status(400).json({error: error.message})
	}

	next(error)
}

module.exports = {
	errorHandler
}

で、上記のmiddlewareはapp.jsというファイルでuseしているので適用されるはずです。

app.js
const express = require('express')
const app = express()
const mongoose = require('mongoose')
const cors = require('cors')
const blogsRouter = require('./controllers/blogs')
const usersRouter = require('./controllers/users')
require('express-async-errors') //<- express-async-errorsはここでrequire
const config = require('./utils/config')
const middleware = require('./utils/middleware')



mongoose.connect(config.MONGODB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, useCreateIndex: true })

app.use(cors())
app.use(express.json())
app.use('/api/blogs', blogsRouter)
app.use('/api/users', usersRouter)
app.use(middleware.errorHandler) //<-これ

module.exports = app

ちなみにエントリーポイントのindex.jsはこういう感じです。ここは特に問題なさそう

index.js
const http = require('http')
const app = require('./app')
const config = require('./utils/config')

const server = http.createServer(app)
server.listen(config.PORT, () => {
	console.log(`Server running on port ${config.PORT}`)
})

app.jsがどうも怪しそうなのですが、ミドルウェアのuseの順番もとくに問題なさそうですし、Expressのドキュメントを色々確認してみてもいまいち原因が掴めないまま時間が過ぎました。
で、そもそもexpress-async-errorsの使い方が何か間違っているのではと思い始め、express-async-errorsのドキュメントを確認しました。すると

スクリーンショット 2021-03-11 18.59.45.png

Then require this script somewhere before you start using it

修正

app.js
const express = require('express')
require('express-async-errors')
const app = express()
const mongoose = require('mongoose')
const cors = require('cors')
const blogsRouter = require('./controllers/blogs')
const usersRouter = require('./controllers/users')
const config = require('./utils/config')
const middleware = require('./utils/middleware')



mongoose.connect(config.MONGODB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, useCreateIndex: true })

app.use(cors())
app.use(express.json())
app.use('/api/blogs', blogsRouter)
app.use('/api/users', usersRouter)
app.use(middleware.errorHandler)

module.exports = app

ドキュメントをちゃんと読んでない&そもそもNode.jsのrequireの仕組みについて理解が浅いというのが招いたミスでした。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?