Пользовательский скаляр GraphQL

RedDeveloper
14.08.2023 14:49
Пользовательский скаляр GraphQL

Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.

GraphQL по умолчанию предоставляет определенные скалярные типы, такие как String, Int, Float и т.д., но обычно разработчикам требуются пользовательские скаляры. В этой статье мы рассмотрим, как их можно создать

Объявление пользовательских скаляров в схеме

Сначала нам необходимо объявить пользовательские скаляры в файле схемы.

В данном примере мы будем использовать скаляр даты, имеющий вид yyyy-mm-dd. Ниже показано, как можно определить скаляр.

Scalar Date

Затем мы можем использовать его в любом поле. В этом примере мы добавим новое поле publishedTime для объекта book и определим его тип как Date

type Book {
    id: ID!
    name: String!
    pageCount: Int!
    price: Int!
    author: Author!
    publishedDate:Date!
}

Как сериализовать и десериализовать скаляры

Теперь нам необходимо определить, как движок graphql может сериализовать и десериализовать этот скаляр.

Мы создадим конфигурационный файл ScalarConfig, в котором будет храниться конфигурация для скаляров, и определим объект GraphQLScalarType.

public static final GraphQLScalarType DateScalar = GraphQLScalarType.newScalar()
.name("Date")
.description("A custom scalar that handles Date")

Spring использует значение, предоставленное в методе name, чтобы знать, какой объект отвечает за тип скаляра. В нашем случае это Date

public static final GraphQLScalarType DateScalar = GraphQLScalarType.newScalar()
            .name("Date")
            .description("A custom scalar that handles Date")
            .coercing(new Coercing() {
                @Override
                public Object serialize(@NotNull Object dataFetcherResult, @NotNull GraphQLContext graphQLContext, @NotNull Locale locale) throws CoercingSerializeException {
                    try {
                        Instant publishedTime = (Instant) dataFetcherResult;
                        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.systemDefault());
                        return formatter.format(publishedTime);
                    } catch (CoercingSerializeException exception) {
                        throw new CoercingSerializeException("Invalid Input:"+exception.getMessage());
                    }
                }

Acest bloc de cod indică modul în care se serializează tipul scalar. În codul nostru java, câmpul publishedTime este de tip Instant, dar serverul se așteaptă la un scalar de tip Date. Prin urmare, convertim un tip instantaneu într-un scalar de dată folosind această metodă.

Rezultatul interogării pentru un bookDetails arată în felul următor.

{
  "data": {
    "bookById": {
      "publishedDate": "2020-01-23"
    }
  }
}

În obiectul Book, data publicată arată astfel:2020-01-23T15:04:01Z

Аналогично у нас есть новый мутационный api под названием updateBookPublishedDate, который позволяет обновить дату публикации книги. В качестве второго аргумента он принимает дату, тип которой - Date. Нам необходимо преобразовать этот аргумент в Instant.

@Override
    public Object parseValue(@NotNull Object input, @NotNull GraphQLContext graphQLContext, @NotNull Locale locale) throws CoercingParseValueException {
        try{
            return LocalDate.parse((String) input).atStartOfDay(ZoneId.systemDefault()).toInstant();
        } catch (RuntimeException exception) {
            throw new CoercingParseValueException("Invalid Input:"+exception.getMessage());
        }
    }

    @Override
    public Object parseLiteral(@NotNull Value input, @NotNull CoercedVariables variables, @NotNull GraphQLContext graphQLContext, @NotNull Locale locale) throws CoercingParseLiteralException {
        try {
            StringValue stringValue = (StringValue) input;
            LocalDate date = LocalDate.parse(stringValue.getValue());
            return date.atStartOfDay(ZoneId.systemDefault()).toInstant();
        } catch (RuntimeException exception) {
            throw new CoercingParseLiteralException("Invalid Input:"+exception.getMessage());
        }
    }

В этом нам помогут два метода

  • parseValue - принимает переменную входного объекта и преобразует ее в представление времени выполнения Java.
  • parseLiteral - принимает на вход AST-литерал graphql.language.Value и преобразует его в представление времени выполнения Java.

Для более детального понимания рассмотрим пример:

mutation updateBooK($date:Date!) {
    updateBookPublishedDate(id:"book-1", date:$date){
    price
    publishedDate
    }
}

Здесь значение даты является входным объектом переменной, т.е. оно будет передано под объектом переменных graphql в виде this:

{
  "date": "2021-09-10"
}

Поэтому в данном случае будет вызван parseValue

mutation{
 updateBookPublishedDate(id:"book-1", date:"2021-06-12"){
  price
  publishedDate
}
}

Здесь мы инлайним значение поля date. В этом случае будет вызван parseLiteral.

Здесь мы инлайним значение поля date. В этом случае будет вызван parseLiteral.

Как зарегистрировать этот скаляр в автоконфигурации Spring

Теперь нам нужно подключить этот скаляр к конфигурации Spring, чтобы она могла регистрировать и использовать скаляр.

@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
    return builder -> builder.scalar(DateScalar);
}

Как всегда, блок кода можно найти в github link.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?

20.08.2023 18:21

Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией

20.08.2023 17:46

В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.

Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox

19.08.2023 18:39

Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest

19.08.2023 17:22

В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!

Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️

18.08.2023 20:33

Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.

Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами

05.08.2023 16:43

В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции обеспечивают мощный способ выполнения операций на битовом уровне, предлагая более эффективные решения для определенных задач. В этом блоге мы рассмотрим, как...