Express with http response status code when an error occurs in NestJS GraphQL Apollo


NestJS GraphQL Apollo 에서 error 발생시 http response status code로 표현하기

개요

GraphQL 명세에서는 모든 http response status code를 200으로 응답하며, 오류 발생시 errors 배열 안에 상세 명세와 statusCode를 내포하고 있다. 이렇게 하는 이유는 한 번에 여러 quries 또는 mutations를 요청 할 수 있기 때문에 그 중 일부는 성공하고 일부는 실패할 수 있기 때문이다. 하지만 이로 인하여 WAS 외부에서 http response status code로 모니터링을 할 경우 4xx, 5xx 오류를 잡아내기 힘들다. GraphQL에서 권장하는 표준방식은 아니지만 서비스 모니터링을 위해서 errors에 있는 statusCode를 http response status code로 빼내는 방법을 소개하겠다.

주의사항 ApolloClient

하지만, 이 경우 ApolloClient에서는 Network Error로 처리된다.

const [createItem] = useMutation(CREATE_ITEM, {
    onCompleted() {
      notification({ label: '아이템이 추가되었습니다.', type: 'success' });
      router.push('/items/list');
    },
    onError(error) {
      notification({ label: error.message, type: 'error' });
    }
  });

예를 들어서 200으로 응답할 경우 error.message에 서버에서 throw할때 전달한 message가 담겨 있지만, 200이 아닌 경우에는 error.message에는 Response not successful: Received status code xxx 와 같은 메세지가 담겨져 있으며 실제 서버에서 넘겨준 error는 error.networkError.result.errors[0].message 에 있다. (버전에 따라 달라 질 수 있으므로 직접 확인해보길 바란다.) 이렇듯 그냥 서버에서는 다른 응답이 모두 똑같지만 단순히 http response status code만 바꿔준다고 생각하기 쉽지만, 표준명세를 따르는 것이 아니기 때문에 클라이언트에서도 표준이 아닌 방식으로 처리해줘야 한다.

방법

GraphQL ModuleformatResponse를 추가하면 된다.

import { GraphQLResponse } from "apollo-server-core";
import _ from "lodash";

export const graphqlStatusCodeResponse = (response: GraphQLResponse, requestContext) => {
  if (response.errors) {
    response.http = {
      headers: requestContext.response.http.headers,
      status: 500
    };
    const status = _.get(response, 'errors.0.extensions.exception.status', false);
    if (status) response.http.status = status;
  }
  return response;
}

errors에 오류가 여러개 있는 경우라도 첫번째 status만을 가져오는 것으로 구현했다. 좀 더 고도화하려면 errors에 오류가 여러개 있을 경우 status에 대해서 우선순위를 정해서 그중 가장 심각한 또는 신경써야 하는 것을 외부로 노출하면 될 것이다.

이렇게 정의한 function을 GraphQL ModuleformatResponse를 추가하면 된다.

GraphQLModule.forRoot({
  ...
  formatResponse: graphqlStatusCodeResponse,
  ...
})

참고로 이건 v3까지 지원하는 방식이며, v4에서는 willSendResponse를 사용해야 한다.

관련내용: https://www.apollographql.com/docs/apollo-server/migration/#formatresponse-hook

이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)