ペペロミア & memoir開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

GraphQLのエラーハンドリングを追加する

GraphQLではエラーになった場合でも http status 200にしてGraphQLのレスポンスをエラーにして、エラーメッセージを返すのが一般的にである。 ただ、それだと特定のエラーの場合のみフロントエンド側でハンドリングしたい時に不便だなと感じていた。

そこで調べた所、GraphQLでは、extensionsを使用してエラーの情報を返すのが正しいようなので、そちらを実装してみた。

PR

backend

github.com

アプリ

github.com

実装

extensionsは以下を参考に実装した。

techblog.zozo.com

実装は、extensionsに定義したエラーコードを返すようにして、frontend側でハンドリングする方式で対応する。まずは、backend側から実装。 エラーコードの定義追加。

usecase/custom_error/code.go

package custom_error

const (
    CodeDefault = "-1"
    // バリデーションエラー
    CodeValidation = "000001"
    // 無効な認証
    CodeInvalidAuthorization = "000002"
    // Not Found
    CodeNotFound = "000003"
    // Already Exists
    CodeAlreadyExists = "000004"
    // 自身の招待コード
    CodeMyInviteCode = "000005"
)

エラーコードを設定するメソッドを追加。

usecase/custom_error/error.go

type RequestError struct {
    Code    string
    Message string
}

func (re RequestError) Error() string {
    return re.Message
}

func NewRequestError(code string, message string) error {
    return RequestError{Code: code, Message: message}
}

上記でエラーコードの設定まで完了したので、GraphQLのエラーハンドリングにextensionsの情報を追加。

app.go

   srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: resolver}))
    srv.SetErrorPresenter(func(ctx context.Context, e error) *gqlerror.Error {
        err := graphql.DefaultErrorPresenter(ctx, e)
        goc := graphql.GetOperationContext(ctx)

        errorCode := ce.CodeDefault

        var re ce.RequestError
        if errors.As(e, &re) {
            errorCode = re.Code
        }

        err.Extensions = map[string]interface{}{
            "code": errorCode,
        }

        return err
    })

上記を追加してエラーになるRequestを送信すると、画像のようにextensionsにエラーコードの情報が追加される。

https://user-images.githubusercontent.com/19209314/155352452-f619964b-5d84-4566-97db-fa55228c5f11.png

これでbackend側の対応は完了したので、次はfrontend側の対応を行う。
まずは、frontend側でも同様にエラーコードを定義。

src/lib/error.ts

const CodeDefault = '-1' as const;
// バリデーションエラー
const CodeValidation = '000001' as const;
// 無効な認証
const CodeInvalidAuthorization = '000002' as const;
// Not Found
const CodeNotFound = '000003' as const;
// Already Exists
const CodeAlreadyExists = '000004' as const;
// 自身の招待コード
const CodeMyInviteCode = '000005' as const;

export const errorCode = {
  CodeDefault,
  CodeValidation,
  CodeInvalidAuthorization,
  CodeNotFound,
  CodeAlreadyExists,
  CodeMyInviteCode,
};

GraphQLのエラーハンドリングに、extensionsからエラーコードを取得する処理を追加。

src/lib/apollo.ts

  const errorLink = onError((error) => {
    if ((error.graphQLErrors || []).length > 0) {
      const graphQLErrors = error.graphQLErrors || [
        {
          message: 'エラー発生しました',
          extensions: { code: errorCode.CodeDefault },
        },
      ];

      const code = graphQLErrors[0]?.extensions?.code ?? errorCode.CodeDefault;

上記でエラーコードの取得まで完了したので、それに合わせてエラー表示の部分を改修。

f:id:wheatandcat:20220322083605p:plain:w250

これでエラーコード毎の判定が行えるようになった。