Jaki jest sens typu wejściowego w GraphQL?

90

Czy mógłbyś wyjaśnić, dlaczego jeśli argumentem wejściowym mutacji jest obiekt, to powinien to być typ wejściowy ? Myślę, że o wiele prościej reuse typ bez podania identyfikatora.

Na przykład:

type Sample {
  id: String
  name: String
}

input SampleInput {
  name: String
}

type RootMutation {
  addSample(sample: Sample): Sample  # <-- instead of it should be
  addSample(sample: SampleInput): Sample
}

Jest to w porządku w przypadku małych obiektów, ale gdy masz wiele obiektów z ponad 10 właściwościami w schemacie, stanie się to obciążeniem.

Dmitrij Duszkin
źródło
32
Obiekty wejściowe muszą być możliwe do serializacji. Ponieważ obiekty wyjściowe mogą zawierać cykle, nie można ich ponownie użyć jako danych wejściowych.
Jesse Buchanan
1
Jesse, wygląda na to, że wystarczy odpowiedź! Możesz odpowiedzieć i tak to zaznaczam.
Dmitry Dushkin
Zastanawiam się, czy da się połączyć z nim interfejsy
MVCDS 11.04.17

Odpowiedzi:

113

Ze specyfikacji:

Typ obiektu GraphQL (ObjectTypeDefinition) ... nie nadaje się do ponownego wykorzystania [jako dane wejściowe], ponieważ typy obiektów mogą zawierać pola definiujące argumenty lub zawierać odniesienia do interfejsów i unii, z których żaden nie jest odpowiedni do użycia jako argument wejściowy . Z tego powodu obiekty wejściowe mają w systemie odrębny typ.

To jest „oficjalny powód”, ale istnieje kilka praktycznych powodów, dla których nie można użyć typu obiektu jako typu obiektu wejściowego lub użyć typu obiektu jako typu obiektu wejściowego:

Funkcjonalność

Zarówno typy obiektów, jak i typy obiektów wejściowych mają pola, jednak te pola mają różne właściwości, które odzwierciedlają sposób, w jaki te typy są używane przez schemat. Twój schemat może potencjalnie zdefiniować argumenty i jakąś funkcję przeliczającą dla pól typu obiektu, ale te właściwości nie mają sensu w kontekście wejściowym (tj. Nie możesz rozwiązać pola obiektu wejściowego - ma już jawną wartość) . Podobnie, wartości domyślne można podać tylko dla pól typu obiektu wejściowego, a nie dla pól typu obiektu.

Innymi słowy, może się to wydawać powielaniem:

type Student {
  name: String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade
}

Jednak dodanie funkcji specyficznych dla typów obiektów lub typów obiektów wejściowych jasno pokazuje, że zachowują się one inaczej:

type Student {
  name(preferred: Boolean): String
  grade: Grade
}

input StudentInput {
  name: String
  grade: Grade = F
}

Wpisz ograniczenia systemowe

Typy w GraphQL są pogrupowane w typy wyjściowe i typy wejściowe .

Typy danych wyjściowych to typy, które mogą być zwracane jako część odpowiedzi wygenerowanej przez usługę GraphQL. Typy danych wejściowych to typy, które są prawidłowymi danymi wejściowymi dla argumentów pól lub dyrektyw.

Te dwie grupy nakładają się na siebie (tj. Skalary, wyliczenia, listy i wartości inne niż null). Jednak typy abstrakcyjne, takie jak związki i interfejsy, nie mają sensu w kontekście wejściowym i nie mogą być używane jako dane wejściowe. Oddzielenie typów obiektów i typów obiektów wejściowych pozwala upewnić się, że typ abstrakcyjny nigdy nie jest używany, gdy oczekiwany jest typ wejściowy.

Projekt schematu

Podczas reprezentowania encji w schemacie jest prawdopodobne, że niektóre encje rzeczywiście „współużytkują pola” między swoimi typami danych wejściowych i wyjściowych:

type Student {
  firstName: String
  lastName: String
  grade: Grade
}

input StudentInput {
  firstName: String
  lastName: String
  grade: Grade
}

Jednak typy obiektów mogą (iw rzeczywistości często to robią) modelować bardzo złożone struktury danych:

type Student {
  fullName: String!
  classes: [Class!]!
  address: Address!
  emergencyContact: Contact
  # etc
}

O ile struktury te mogą przekładać się na odpowiednie dane wejściowe (tworzymy Studenta, więc przekazujemy również obiekt reprezentujący jego adres), często tak nie jest - tj. Może musimy określić zajęcia ucznia za pomocą ID klasy i ID sekcji, a nie obiekt. Podobnie, możemy mieć pola, które chcemy zwrócić, ale nie chcemy mutować, lub odwrotnie (jak passwordpole).

Co więcej, nawet w przypadku stosunkowo prostych jednostek często mamy różne wymagania dotyczące dopuszczalności wartości zerowej między typami obiektów i ich „odpowiednikami” obiektami wejściowymi. Często chcemy zagwarantować, że pole zostanie również zwrócone w odpowiedzi, ale nie chcemy, aby te same pola były wymagane w naszych danych wejściowych. Na przykład,

type Student {
  firstName: String!
  lastName: String!
}

input StudentInput {
  firstName: String
  lastName: String
}

Wreszcie, w wielu schematach często nie ma mapowania jeden do jednego między typem obiektu a typem obiektu wejściowego dla danej encji. Typowym wzorcem jest wykorzystanie oddzielnych typów obiektów wejściowych dla różnych operacji w celu dalszego dostrojenia walidacji danych wejściowych na poziomie schematu:

input CreateUserInput {
  firstName: String!
  lastName: String!
  email: String!
  password: String!
}

input UpdateUserInput {
  email: String
  password: String
}

Wszystkie te przykłady ilustrują ważną kwestię - chociaż typ obiektu wejściowego może czasami odzwierciedlać typ obiektu, znacznie mniej prawdopodobne jest, że zobaczysz go w schematach produkcyjnych ze względu na wymagania biznesowe.

Daniel Rearden
źródło
4
to naprawdę świetna odpowiedź!
Charlie Ng
Popraw mnie, jeśli się mylę, ale w twoim przykładzie typ Gradenie może być ponownie użyty w danych wejściowych StudentInput, prawda? Będziesz musiał albo wstawić pola w obiekcie wejściowym, albo mieć GradeInputobiekt wejściowy.
Matt
2
@Matt Dobre pytanie! W powyższym przykładzie Gradejest typem wyliczenia. W przeciwieństwie do obiektów, typy skalarne (takie jak String, Int itp.) I typy wyliczeniowe mogą być używane zarówno jako typy wejściowe, jak i wyjściowe.
Daniel Rearden
ładne wyjaśnienie
Visakh Vijayan
Naprawdę świetna odpowiedź!
Johnny
25

Komentarz Jessego jest poprawny. Aby uzyskać bardziej formalną odpowiedź, oto fragment dokumentacji GraphQL na temat typów danych wejściowych :

Zdefiniowany powyżej typ obiektu nie nadaje się do ponownego wykorzystania w tym miejscu, ponieważ obiekty mogą zawierać pola wyrażające odwołania cykliczne lub odwołania do interfejsów i związków, z których żaden nie jest odpowiedni do użycia jako argument wejściowy. Z tego powodu obiekty wejściowe mają w systemie odrębny typ.

AKTUALIZACJA

Odkąd go opublikowałem, stwierdziłem, że odwołania cykliczne są w rzeczywistości dopuszczalne, o ile są zerowe (inaczej zadeklarowałyby nieskończony łańcuch). Jednak nadal istnieją inne ograniczenia (np. Interfejsy), które wydają się wymagać oddzielnego systemu typów danych wejściowych.

LB2
źródło
4
Tutaj jest trochę więcej dyskusji na temat tego, dlaczego istnieje to ograniczenie: github.com/graphql/graphql-js/issues/599
Cam Jackson,