종단 간 GraphQL 오류 처리?

GraphQL 해석기(서버 측에서 GraphQL Yoga 사용)에서 오류를 처리하는 방법을 공유하고 프런트엔드 측(여기서 Svelte + @urql/svelte 사용)으로 전달하고 싶습니다.

백엔드



포장 오류 정보



내가 알아낸 것처럼 GraphQL 끝점에서 오류를 반환하는 유일한 방법은 리졸버에서 오류를 발생시키는 것입니다. 이 디자인은 백엔드와 프런트엔드 간에 오류 데이터를 전송하는 데 몇 가지 제한 사항을 부과합니다. 서버가 처리할 수 없는 잘못된 요청이 와서 오류와 정확히 무엇이 잘못되었는지에 대한 세부 정보를 반환하려고 한다고 가정해 보겠습니다. 다음 인터페이스에서 모든 정보를 압축한다고 가정합니다.

type ApiError = {
   code: 400,
   message: "The request was bad :("
}


제한으로 돌아가서 - 객체와 같은 것을 new Error(arg) 호출에 인수로 넣을 수 있는 방법은 없지만 ApiError 객체를 JSON 문자열로 변환할 수 있습니다. 그것은 모든 정보를 throwable에 패킹할 수 있는 이상한 작은 트릭입니다. 이것이 정보 전송에 관한 전부이지만 코드 구성에 대한 몇 가지 사항이 더 있습니다.
오류를 발생시켜 GraphQL 끝점에서 오류를 반환하는 것은 편리할 수 있으며 Typescript 어설션 함수를 사용하면 유형이 안전할 수 있습니다. return 문을 if 문으로 래핑할 필요가 없습니다. 오류를 던지고 플랫 코드 구조를 유지하십시오. 정보를 오류 개체에 패킹하는 것과 결합해 보겠습니다.

export const newApiError = (errorObject: ApiError) => {
   return new Error(JSON.stringify(errorObject))
}


이제 할 수 있습니다 throw newApiError(apiError) .
하지만 if 문을 던질 필요가 있는지 확인하는 것은 여전히 ​​필요합니다. 더 잘할 수 있을까요? 예, typescript 어설션 기능을 사용합니다.

if 문 없애기



엔드포인트에서 요청을 수신하는 백엔드에 권한 부여가 필요하지만 요청에 자격 증명이 없다고 가정해 보십시오. "401: 인증되지 않음, 신임장 없음, 통과하지 못할 것입니다."라고 말하고 싶은 것 같습니다. 그렇다면 리졸버에서 가장 쉬운 방법은 무엇입니까? 다음과 같이 가정합니다.

// get an auth token somehow
const authToken string | null = req.headers.get("Authorization") 
assert(
  authToken,
  "401: There are no creds, you shall not pass"
)



커스텀 어설션



그러나 그것은 유일한 문자열입니다. 객체와 같은 것을 assert 호출에 전달할 수 없으므로 assert 구현을 작성해 보겠습니다.

export function assertWithApiError<T>(
    statement: T | null | undefined,
    errorObject: ApiError
): asserts statement is T {
    if (!statement) {
        throw newApiError(errorObject)
    }
}



Typescript assert 함수를 사용하면 데이터가 오류로 묶인 상태에서 직접 생성한 모든 throwable을 던질 수 있는 typesafe assertion을 사용할 수 있습니다.

// get an auth token somehow
const authToken: string | null = req.headers.get("Authorization")
assertWithApiError(
  authToken, 
  { message: 'Auth token is not presented', code: 401 }
)
// there is typescript knows that token is a string



프런트엔드



프런트 엔드 부분은 어떻습니까? 위에서 말했듯이 @svelte/urql 패키지를 사용하여 svelte 앱에서 GraphQL을 처리합니다. 코드를 살펴봅시다!
일반적인 @svelte/urql 사용 사례로 시작하십시오.

// mutation function is from @svelte/urql package
const editUserMutation = mutation<{ me: User }>({
   query: EDIT_USER,
})

const { data, error } = await editMeMutation({ intro }) 


따라서 GraphQL 원래 오류의 배열을 포함하는 @svelte/urql의 오류CombinedError 유형이 있습니다.


type OriginalError = {
   message: string
   stack: string
}

export const parseGraphqlApiErrors = (error: CombinedError): ApiError[] => error.graphQLErrors.map((e) => {
   const rawOriginalError = (e.extensions?.originalError as OriginalError).message
   return parseApiError(rawOriginalError)
})


export const parseApiError = (jsonString: string): ApiError => {
   try {
      const parsed: unknown = JSON.parse(jsonString)
      if (apiErrorTypeGuard(parsed)) {
         return parsed
      } else {
         throw new Error('got invalid api error')
      }
   } catch (e) {
      console.error(e)
      throw Error("Can't parse api error from json string")
   }
}

const apiErrorTypeGuard = (possiblyError: any): possiblyError is ApiError =>
   typeof possiblyError === 'object' && 'code' in possiblyError && 'message' in possiblyError



마지막에는 원하는 데이터로 채워진 오류를 발생시키는 사용자 지정 어설션과 해당 데이터를 추출할 수 있는 프런트엔드 코드가 있습니다. 그게 다야.


저는 GraphQL, Svelte 또는 Urql의 숙련된 사용자가 아닙니다. 위에서 설명한 것보다 더 나은 기존 솔루션을 알려주시고 제 아이디어가 누군가에게 도움이 되기를 바랍니다 :)

사진 제공 Mario Mendez

좋은 웹페이지 즐겨찾기