GraphQL - Graphene: 7(Execution: Query Validation)

Jihun Kim·2022년 5월 1일
0

GraphQL

목록 보기
15/16
post-thumbnail

Query Validation

GraphQL로 쿼리를 실행하면 Query AST가 쿼리가 유효 하며 실행 가능한 상태인지 확인한다. 아래에 자주 사용되는 validator와 custom validator에 대해 소개한다.

추상 구문 트리(abstract syntax tree, AST)란?(위키백과 참고)

  • 추상 구문 트리는 프로그래밍 언어로 작성된 소스 코드의 추상 구문 구조의 트리이다.
  • 각 노드는 소스 코드에서 발생되는 구조를 나타낸다.
  • 여기서 '추상적'이라는 표현이 붙은 이유는 실제 구문에서 나타나는 모든 세세한 정보를 나타내지는 않기 때문이다.
    - 예를 들어, 그룹핑을 위한 괄호는 암시적으로 트리 구조를 갖지만 분리된 노드로 표현되지는 않는다.
    - if-condition-then 표현식과 같은 구문 구조는 3개의 가지에 1개의 노드가 달린 구조로 표기된다.

추상 구문 트리(abstract syntax tree, AST) by 위키백과


Depth Limit Validator

우리 서비스에 GraphQL을 도입할 지 말 지를 논의할 때 문제가 되었던 것 중 하나가 depth limit을 설정해야 하지 않냐는 것이었다. Depth Limit Validator를 이용하면 해결할 수 있겠다.

그런데... graphene-django에서 이를 적용하는 데에는 어려움이 있다고 한다. 해결 방법은 뒤에서 찾아보기로 한다.

이 validator에서 사용하는 arguments는 다음과 같다.

  • max_depth
    - 쿼리할 수 있는 최대 depth
  • ignore
    - ignore에 정의된 필드명이 등장하면 그전까지 진행되던 재귀적 depth 검사를 중지하기 위해 사용할 수 있다.
    - 이름과 일치하는 문자열, 정규 표현식(regexp) 또는 boolean 값을 반환하는 함수 중 하나가 될 수 있다.
  • callback
    - validation이 실행될 때마다 호출 된다.
    - callback 인자를 통해 각 작업의 depths의 map에 대한 정보를 갖는 object를 받을 수 있다.
from graphql import validate, parse
from graphene import ObjectType, Schema, String
from graphene.validation import depth_limit_validator


class MyQuery(ObjectType):
    name = String(required=True)


schema = Schema(query=MyQuery)

# queries which have a depth more than 20
# will not be executed.

validation_errors = validate(
    schema=schema.graphql_schema,
    document_ast=parse('THE QUERY'),
    rules=(
        depth_limit_validator(
            max_depth=20
        ),
    )
)

Custom Validators

  • validation을 수행하기 위해서는 커스텀하게 정의하려는 validation class 내에 enter_* 또는 leave_* 메소드가 반드시 정의되어야 한다.
  • validation이 실패하는 경우에 대해 정의하기 위해서는 validator의 report_error 메소드를 호출해야 한다(가령, 클라가 해당 필드는 호출하지 못하도록 하려는 경우).
    - report_error 메소드에 GraphQLError 인스턴스를 인자로 주어야 하는데 이 때 실패 이유를 표시해서 함께 호출하면 된다.

아래 예시는 특정 필드가 블랙리스트에 포함된 경우 validation이 실패하도록 만든다.

from graphql import GraphQLError
from graphql.language import FieldNode
from graphql.validation import ValidationRule


my_blacklist = (
    "disallowed_field",
)


def is_blacklisted_field(field_name: str):
    return field_name.lower() in my_blacklist


class BlackListRule(ValidationRule):
    def enter_field(self, node: FieldNode, *_args):
        field_name = node.name.value
        if not is_blacklisted_field(field_name):
            return

        self.report_error(
            GraphQLError(
                f"Cannot query '{field_name}': field is blacklisted.", node,
            )
        )

GraphQLError

위와 같은 방법으로 validation을 할 수도 있지만, 찾아보니 GraphQLError를 사용하는 방법이 더 많이 언급된다.
예를 들어, Mutation에 대한 error_message를 주고 싶은 경우 아래와 같은 방법을 사용하면 된다.

from graphql import GraphQLError
...
raise GraphQLError(message="User name already exists", extensions={"code": 400})
profile
쿄쿄

0개의 댓글