Oba mają pomóc w zaprojektowaniu schematu z heterogenicznym zbiorem typów, a można osiągnąć tę samą funkcjonalność przy użyciu obu, ale GraphQLInterfaceType
jest bardziej odpowiedni, gdy typy są w zasadzie takie same, ale niektóre z pól są różne, i GraphQLUnionType
, gdy typy są całkowicie różne i mają zupełnie różne pola.
Ostatecznie, czy użyć jednego lub drugiego w zależności od projektu schematu.
Przykładowo, powiedzmy, że masz listę blogów, ale blogi korzystające z frameworka A używają nazwy użytkownika i hasła jako uwierzytelniania, a blog używający frameworka B używa adresu e-mail i hasła. Projektujemy je z GraphQLInterfaceType
tak:
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
password: { type: new GraphQLNonNull(GraphQLString) }
},
resolveType: resolveBlogType
});
const BlogAType = new GraphQLObjectType({
name: 'BlogA',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const BlogBType = new GraphQLObjectType({
name: 'BlogB',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
function resolveBlogType(value) {
return value.username ? BlogAType : BlogBType;
}
Kiedy tworzymy nowy blog wysyłania username
, stworzy BlogA
.
Możemy zapytać tak:
query MyQuery {
blogs: {
url
password
... on BlogA {
email
}
... on BlogB {
username
}
}
}
Teraz przejdźmy taką samą funkcjonalność, ale stosując GraphQLUnionType
, bo wolimy używać po prostu jeden rodzaj bloga oraz 2 rodzaje metod uwierzytelniania:
const AuthAType = new GraphQLObjectType({
name: 'AuthA',
fields: {
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthBType = new GraphQLObjectType({
name: 'AuthB',
fields: {
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthType = new GraphQLUnionType({
name: 'Auth',
types: [AuthAType, AuthBType]
resolveType: resolveAuthType
});
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
auth: { type: AuthType }
},
});
function resolveAuthType(value) {
return value.username ? AuthAType : AuthBType;
}
Możemy zapytać tak:
query MyQuery {
blogs: {
url
auth {
... on AuthA {
username
password
}
... on AuthB {
email
password
}
}
}
}
jak widać w w tym przykładzie osiągamy to samo z interfejsem lub złączem, ale jeden lub drugi może być bardziej odpowiedni w zależności od projektu schematu.
Załóżmy na przykład, że chcesz dodać strukturę blogu C, która również używa adresu e-mail i hasła. Będziesz musiał dołączyć inne pole, aby odróżnić je od bloga B w naszej funkcji resolveBlogType
. Dodajmy pole type
. W naszym przykładzie unijnym, ponieważ mamy dostęp tylko do pól w Unii, należy dodać do Unii type
. Gdyby w przyszłości chcieliśmy dodać kolejną Unię z tymi samymi polami dla wielu frameworków, musielibyśmy również dodać tam pole type
. Niezbyt miłe, że w naszym schemacie są wielokrotnie powielane. Lepszym rozwiązaniem może być użycie interfejsu i posiadanie pojedynczego pola type
dostępnego w funkcji resolveBlogType
przez wszystkie obiekty korzystające z interfejsu.
kiedy szukasz "blogów" na fragmencie przekaźnika, co to odpowiada grafql-mądry? Widzę, że twój typ interfejsu grafql nazywa się 'Blog' – maxwell
Musisz utworzyć zapytanie' blogs', którego nie ma w przykładowym kodzie –
Wciąż nie jestem pewien, czy rozumiem – arcom