Каков общий рабочий процесс с клиентским приложением Typescript и бэкэндом GraphQL - PullRequest
1 голос
/ 03 апреля 2019

У меня есть клиентское приложение React Typescript. И я читал, что хорошей практикой является создание интерфейсов для ответов, поступающих с конечных точек.

Например, схема GraphQL объявила этот тип:

type Author {
  id: Int!
  firstName: String!
  lastName: String!
  ... {more fields}
  posts(findTitle: String): [Post]
}

Так что на стороне клиента мне также нужно создать аналогичный интерфейс:

interface Post: {...}

interface IAuthor {
  id: number;
  firstName: string;
  lastName: string;
  posts: Post[]
}

И когда клиентское приложение получает ответ, оно просто передает ответ этому интерфейсу, например:

const data = await getWholeAuthorFromGraphQL();
const user = data as IUser;

Но мне не ясно, что GraphQL позволяет извлекать определенные поля вместо целого объекта.

Например, в другом месте приложения мне также необходим объект IUser, но только с 2 полями: firstName и secondName. Должен ли я создать другой интерфейс в этом случае? Нравится:

interface AuthorFullname {
  firstName: string;
  lastName: string;
}

Или я могу просто привести к IUser все, что относится к Author GQL-типу? И тогда в коде я буду осторожен, потому что буду знать, что user объект содержит только firstName и lastName. Поэтому я не буду использовать другие поля, кроме firstName и lastName.

Ответы [ 3 ]

1 голос
/ 03 апреля 2019

Если вы согласны с созданием кода, в своем клиентском приложении вы можете использовать инструмент под названием apollo codegen .

Обычно он проверяет каждое место, где вы определили свои запросы gql, и вместе с определением сервера schema генерирует необходимые Interfaces для определенных запросов. Таким образом, если вы определите вложенные запросы или запросы с фрагментами, они просто сгенерируют интерфейсы, необходимые для их выполнения.

Вот также хорошая статья о Создание определений TypeScript для запросов GraphQL .

0 голосов
/ 03 апреля 2019

Один из подходов - просто создать интерфейсы, специфичные для каждого запроса, который вы фактически используете в своем коде. Например, даны два запроса:

query QueryA {
  allAuthors {
    id
    firstName
    lastName
    posts {
      ...PostFields
    }
  }
}

query QueryB {
  id
  firstName
  lastName
}

Мы можем просто создать два отдельных интерфейса, по одному для каждого запроса:

interface QueryAAuthor {
  id: number;
  firstName: string;
  lastName: string;
  posts: QueryAPost[];
}

interface QueryBAuthor {
  id: number;
  firstName: string;
  lastName: string;
}

Хотя это создает больше интерфейсов и создает дополнительную сложность в вашем коде, это также означает, что при работе с результатом запроса не возникает двусмысленности. Например, если бы вместо этого мы использовали один интерфейс с Partial, я все равно мог бы ссылаться на posts даже при работе с результатом QueryB, и компилятор не мог его поймать.

Это на самом деле подход, используемый функцией Codegen Apollo CLI , которая позволяет генерировать типы на основе схемы и ваших запросов на стороне клиента.

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

0 голосов
/ 03 апреля 2019

Это отличный вопрос, учитывая, что интерфейс GraphQL потенциально может возвращать частичные версии интерфейса, лучше использовать типы HOC Typescript, такие как Partial

const getResult : Partial<MyModel> = await yourNetworkRequest()

В этом случае Partial сделает каждый атрибутнеобязательно, что означает, что вы можете выполнить проверку существования на фактическом сайте вызова для ответа.Это особенно полезно, если у вас установлен Typescript в строгом режиме.

В качестве альтернативы вы можете использовать свой вызов

function myNetworkCall() : Partial<MyModel>

, который снова применяет то же самое, затем вам придется выполнить тяжелую работу, чтобыубедитесь, что поля, которые вы ищете, существуют (проверяя существование)

// assume the following
interface MyModel {
 field1: string;
 field2: string;
}




const item = myNetworkCall()
if(item.field1){
 // etc
}
...