NUXT3で$fetchを含む例外処理でエラーメッセージを取り出す作業が手間なので、useFetchErrorMessageというコンポーザブルを書いて使いまわしてる話。

JavaScript

NUXT3から$fetchを使って、APIサーバからデータを取得した際に、エラーが返ってきた際のエラーメッセージの取得が毎度面倒だったので、コンポーザブルにして使いまわすことにしました。

APIサーバはexpressを使っていて、以下のようにhttp-errorモジュールを使用してエラーを返却しています。

// エラー
const createError = require('http-errors')

try {
  ...
} catch (err) {
  next(createError(500, 'XXXXに失敗しました。'))
  return
}
// デフォルトのエラーの処理
app.use((err, _req, res, next) => {
  if (res.headersSent) {
    return next(err)
  }
  res
    .status(err.statusCode || 500)
    .send(err.message || 'Internal Server Error')
})

NUXT2で$axiosを使用していた時は、err.data.messageでAPIから返却されたエラーメッセージが取得できましたが、NUXT3で$fetchを使用した場合は、err.dataで取得できるようです。また、$fetchからthrowされるエラーは「EetchError」classのインスタンスのようです。

「$fetch」のみを「try ~ catch」で囲むのであれば、エラー値をEetchError のインスタンスと決め打ちして書けば良いのですが、その前後もまとめて囲みたい場合は、エラーの中身に合わせて処理を分けてやる必要があります。

今回作ったコンポーザブルは、エラーが発生した際に表示するエラーメッセージをエラー値から取り出すためのものになります。

私は例外をthrowする際は、throw new Error(‘XXX’)とエラーオブジェクトを生成するスタイルで書いていますが、念のために文字列がthrowされた場合にも対応しておくこととします。

また、$fetchでアップロードする場合に、途中でキャンセルできるような実装を行うこともあるかと思います。そのような場合にも対応しようと思います。(最新のChrome、Firefox、iOSではテストしましたが、Androidの各種ブラウザで動作するかは不明です)

import { FetchError } from 'ofetch'

export const useFetchErrorMessage = (error: any, defaultErrorMessage: string): string => {
    // エラーメッセージを初期化する
    let errorMessage = ''

    if (error instanceof FetchError) {
        // FetchErrorの場合
        const { cause, data, message } = error
        if (cause && ((cause as DOMException).message || '').indexOf('abort') > -1) {
            // ファイルアップロードを中断した場合の処理
            // Abort時の動作がブラウザによって異なるので、上記の条件で拾いきれるかは不明...
            errorMessage = 'ファイルのアップロードを中止しました。'
        } else if (typeof data === 'string' || data instanceof String) {
            // APIから返却されたエラーメッセージを取り出す
            errorMessage = data as string
        } else {
            // その他の場合の処理
            errorMessage = message
        }
    } else if (error instanceof Error) {
        // Errorがthrowされた場合の処理
        errorMessage = error.message
    } else if (typeof error === 'string' || error instanceof String) {
        // 文字列がthrowされた場合の処理
        errorMessage = error as string
    }

    return errorMessage || defaultErrorMessage
}

引数にエラーオブジェクトとデフォルトのエラーメッセージを指定して使います。

try {
    ....
    $fetch('...')
    ....
} catch (err) {
    ...
    const errorMessage = useFetchErrorMessage(err, '送信に失敗しました。')
}

これでかなり楽になりました!

コメント

タイトルとURLをコピーしました