Ответ типа GraphQL / борьба с фрагментами - PullRequest
0 голосов
/ 13 декабря 2018

У меня есть некоторые проблемы с написанием API в graphql.

Каждый ответ в моем API должен выглядеть примерно одинаково.Так что в идеале это будет тип графика:

type Response {
  success
  data {
    ... always different
  }
  errors {
    path
    message
  }
}

Но поскольку поле данных здесь всегда отличается.У каждой мутации / запроса должен быть свой тип ответа (если я правильно понимаю graphql).

Так что для входа в систему это тип, который я создаю с помощью функции преобразования:

type LoginResponse {
  success
  data {
    user
    token
  }
  errors {
    path
    message
  }
}

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

fragment Response on LoginResponse {
  success
  errors {
    path
    message
  }
}

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

Возможно, кто-то уже боролся с этим или есть лучшая практика для этого, которую я не вижу

Ответы [ 2 ]

0 голосов
/ 22 декабря 2018

Хорошее видео о том, как обрабатывать ошибки (связанные с этим вопросом): https://www.youtube.com/watch?v=-wRXk_QZ3Ko

0 голосов
/ 13 декабря 2018

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

Распространенным шаблоном, который вы видите на схемах, является идея интерфейса Node.У вас может быть запрос на выборку узла по идентификатору, например:

type Query {
  node(id: ID!): Node
}

interface Node {
  id: ID!
}

type Foo implements Node {
  id: ID!
  foo: String!
}

type Bar implements Node {
  id: ID!
  bar: Int!
}

Здесь Node может быть либо Foo, либо Bar, поэтому, если бы мы записали фрагментдля Node это может выглядеть примерно так:

fragment NodeFields on Node {
  id # id is part of the interface itself
  ... on Bar {
    bar # fields specific to Bar
  }
  ... on Foo {
    foo # fields specific to Foo
  }
}

Если у вас нет общих полей, вы можете использовать Союз вместо того же эффекта:

union SomeUnion = Foo | Bar

Таким образом, чтобы облегчить некоторые повторения в вашем внешнем коде, вы могли бы сделать каждый из ваших Result типов интерфейсом или, что еще лучше, иметь один тип Result с data, являющимся объединением.К сожалению, ни Интерфейсы, ни Союзы не работают со Скалярами или Списками, что усложняет ситуацию, если data должен быть Скаляр или Список для некоторых запросов.

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

  1. GraphQL уже возвращает результат запроса в виде объекта JSON со свойствами data и errors.
  2. Возвращение ошибок внутриGraphQL data потребует дополнительной логики для захвата и форматирования ошибок, в отличие от возможности просто генерировать ошибку в любом месте и иметь GraphQL для обработки сообщений об ошибках для вас.
  3. Вы не сможетечтобы фиксировать ошибки проверки, так что вы потенциально можете получить ошибки в двух местах - внутри массива errors и внутри data.errors.Это также означает, что ваш клиент должен искать ошибки в двух местах, чтобы правильно обрабатывать ошибки.
  4. GraphQL специально разработан для частичного разрешения ответа.Это означает, что даже если некоторые части ответа ошибочны и не могут быть разрешены, другие могут быть разрешены и возвращены как часть ответа.Это означает, что концепция «успешности» ответа на самом деле не применима в GraphQL.Если вам абсолютно необходимо поле success, было бы гораздо лучше использовать что-то вроде formatResponse, чтобы добавить его к объекту ответа после разрешения запроса.

Это значительно упростит задачупридерживайтесь соглашения и структурируйте вашу схему следующим образом:

type Query {
  login: LoginResponse
}

type LoginResponse {
  token: String
  user: User
}

Фактический ответ будет по-прежнему включать data и errors:

{
  "data": {
    "login": {
      "token": "",
    }
  },
  "errors": []
}

Если вам даже нужно использоватьфрагменты, вам все равно понадобится один фрагмент для каждого типа, но между фрагментами будет значительно меньше повторений.

...