GraphQL

Promh90·2021년 11월 29일
0

spring boot

목록 보기
4/4

GraphQL

  • GQL(GraphQL)로 불리는 API용 쿼리 언어로 특정 datasource에 종속되어 있지 않으며 서버측 런타임 라이브러리 형태로 제공하는 쿼리언어. Facebook에서 제작

  • 기존의 REST API는 클라이언트가 리소스를 엊기 위해 서버에서 정의한 여러 엔드포인트로 호출하는 형태였다면 GQL은 서로 협의한 Schema의 정보를 가지고 다양하게 동적 쿼리 를 하는 형태이다.

  • 마케팅/광고 도메인의 플랫폼개발시 주로 사용하는 기술로 고객 니즈를 파악에 도움되는 것으로 보임

기본용어

  • Query: R Operation
  • Mutation: CUD Operation
  • Resolver: 서버에서 구현하며, 클라이언트의 요청에 따른 데이터 매핑의 역할
  • Type: 서버에서 응답하는 데이터의 모양(구조), 다른 Type의 필드를 포함할 수 있다.
  • Input: Type과 같지만, 서버로 요청을 보내는 데이터의 모양(구조)
  • Scalar: primitive type (String, Int, Boolean, Float 등)
  • Interface: 필드의 이름과 인수등을 저장하며 GQL 객체가 이를 상속하여 특정 필드 사용할 수 있는 형태 제공
  • Schema: Query, Mutation을 관리하며 서버에서 실행할 수 있는 항목들을 정의

CRUDL (Create, Retrieve, Update, Delete, List) 구현

dependency

  • graphql-java-annotations

    • java언어를 지원하는 library로 현재 Github Stars(5.2k)가 제일 많음
  • spring boot graphql starter 라이브러리도 제공 추후 스터디를 해서 포스팅하는걸로

maven

  • graphql-java-annotations
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-annotations</artifactId>
    <version>${graphql-java-annotations.version}</version>
</dependency>
  • http server
<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.6</version>
</dependency>

schema loading

public class UserSchema {

    private final GraphQLSchema schema;

    public UserSchema() throws IllegalAccessException, NoSuchMethodException, InstantiationException {
        schema = newSchema().query(GraphQLAnnotations.object(UserQuery.class))
            .mutation(GraphQLAnnotations.object(UserMutation.class))
            .build();
    }

    public GraphQLSchema getSchema() {
        return schema;
    }
}

Type

@Data
@GraphQLName(SchemaUtils.USER)
public class User {

    @GraphQLField
    private Long id;
    @GraphQLField
    private String name;
    @GraphQLField
    private String email;
    @GraphQLField
    private Integer age;
}

resolver

@Override
public void handle(Context context) throws Exception {
    context.parse(Map.class)
      .then(payload -> {
          Map<String, Object> parameters = (Map<String, Object>)
            payload.get("parameters");
          ExecutionResult executionResult = graphql
            .execute(payload.get(SchemaUtils.QUERY)
              .toString(), null, this, parameters);
          Map<String, Object> result = new LinkedHashMap<>();
          if (executionResult.getErrors().isEmpty()) {
              result.put(SchemaUtils.DATA, executionResult.getData());
          } else {
              result.put(SchemaUtils.ERRORS, executionResult.getErrors());
              LOGGER.warning("Errors: " + executionResult.getErrors());
          }
          context.render(json(result));
      });
}

query & mutation

  • query
@GraphQLName(SchemaUtils.QUERY)
public class UserQuery {

    @GraphQLField
    public static User retrieveUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id) {
        final Optional<User> any = getUsers(env).stream()
            .filter(c -> c.getId() == Long.parseLong(id))
            .findFirst();
        return any.orElse(null);
    }
    
    @GraphQLField
    public static List<User> searchName(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.NAME) final String name) {
        return getUsers(env).stream()
            .filter(c -> c.getName()
                .contains(name))
            .collect(Collectors.toList());
    }
    
    @GraphQLField
    public static List<User> listUsers(final DataFetchingEnvironment env) {
        return getUsers(env);
    }

}
  • mutation
@GraphQLName(SchemaUtils.MUTATION)
public class UserMutation {
    @GraphQLField
    public static User createUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.NAME) final String name, @NotNull @GraphQLName(SchemaUtils.EMAIL) final String email, @NotNull @GraphQLName(SchemaUtils.AGE) final String age) {
        List<User> users = getUsers(env);
        final User user = new User(name, email, Integer.valueOf(age));
        users.add(user);
        return user;
    }

    @GraphQLField
    public static User updateUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id, @NotNull @GraphQLName(SchemaUtils.NAME) final String name, @NotNull @GraphQLName(SchemaUtils.EMAIL) final String email,
        @NotNull @GraphQLName(SchemaUtils.AGE) final String age) {
        final Optional<User> user = getUsers(env).stream()
            .filter(c -> c.getId() == Long.parseLong(id))
            .findFirst();
        if (!user.isPresent()) {
            return null;
        }
        user.get()
            .setName(name);
        user.get()
            .setEmail(email);
        user.get()
            .setAge(Integer.valueOf(age));
        return user.get();
    }

    @GraphQLField
    public static User deleteUser(final DataFetchingEnvironment env, @NotNull @GraphQLName(SchemaUtils.ID) final String id) {
        final List<User> users = getUsers(env);
        final Optional<User> user = users.stream()
            .filter(c -> c.getId() == Long.parseLong(id))
            .findFirst();
        if (!user.isPresent()) {
            return null;
        }
        users.removeIf(c -> c.getId() == Long.parseLong(id));
        return user.get();
    }
}

request & response

  • request (endpoint url을 users로 설정)
curl --location --request POST 'http://localhost:5050/users' \
--header 'Content-Type: application/json' \
--data-raw '{
  "query": "mutation($name: String! $email: String! $age: String! ){ createUser ( name: $name email: $email age: $age) { id name email age } }",
  "parameters": {
    "name": "John",
	"email": "john@email.com",
    "age": 34
  }
}'
  • response
    200ok
{
    "data": {
        "createUser": {
            "id": 1,
            "name": "John",
            "email": "john@email.com",
            "age": 34
        }
    }
}

ref

profile
재밌고 편하게~ 나의 개발 기록 Velog

0개의 댓글