Можем да създадем прост GraphQL сървър с Express. За да направим това, имаме нужда от пакетите express-graphql
и graphql
.
В тази статия ще разгледаме как да добавяме типове, които можем да използваме за изграждане на схема с пакета graphql
.
Типове конструиране
Можем да конструираме схема програмно с конструктора GraphQLSchema
, който идва с пакета graphql
.
Вместо да дефинираме Query
и Mutation
типове с помощта на езика на схемата, можем да ги създадем като отделни типове обекти.
Например, можем да напишем следното, за да създадем тип с конструктора graphql.GraphQLObjectType
, за да създадем тип обект програмно:
const express = require('express'); const graphqlHTTP = require('express-graphql'); const graphql = require('graphql'); const userType = new graphql.GraphQLObjectType({ name: 'User', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, } }); let users = { '1': { id: '1', name: 'Jane' } } const queryType = new graphql.GraphQLObjectType({ name: 'Query', fields: { user: { type: userType, args: { id: { type: graphql.GraphQLString } }, resolve: (_, { id }) => { return users[id]; } } } }); const schema = new graphql.GraphQLSchema({ query: queryType }); const app = express(); app.use('/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, })); app.listen(3000, () => console.log('server started'));
В кода по-горе създадохме типа данни userType
GraphQL, като написахме:
const userType = new graphql.GraphQLObjectType({ name: 'User', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, } });
Полето name
дефинира името на нашия тип, а обектът fields
има полетата, които включваме с типа. Дефинирахме id
и name
и двете да имат тип String
.
След това дефинираме нашия тип Query
с:
const queryType = new graphql.GraphQLObjectType({ name: 'Query', fields: { user: { type: userType, args: { id: { type: graphql.GraphQLString } }, resolve: (_, { id }) => { return users[id]; } } } });
В кода по-горе дефинирахме name
на типа да бъде Query
. fields
, което включваме, е полето user
, което е от тип User
, който сме дефинирали по-горе.
Освен това уточнихме, че имаме низа id
като аргумент със свойството args
.
И накрая, имаме свойство resolve
с резолвера, за да върнем това, което искаме да върнем.
В този случай искаме да върнем User
от обекта users
, като се има предвид id
, предадено в аргумента.
След това, когато направим следната заявка:
{ user(id: "1"){ id name } }
Връщаме се:
{ "data": { "user": { "id": "1", "name": "Jane" } } }
тъй като имаме следното в обекта users
:
let users = { '1': { id: '1', name: 'Jane' } }
Можем да направим същото с мутациите.
Това е особено полезно, ако искаме да създадем GraphQL схема автоматично от нещо друго като схема на база данни. Може да имаме общ формат за нещо като създаване и актуализиране на записи в база данни.
Също така е полезно за внедряване на функции като типове обединение, които не се съпоставят с ES6 конструкции.
GraphQLUnionType
Можем да създаваме обединени типове с конструктора GraphQLUnionType
.
За да създадем тип обединение и да го използваме в нашето приложение, можем да използваме конструктора GraphQLUnionType
, както следва:
const express = require('express'); const graphqlHTTP = require('express-graphql'); const graphql = require('graphql'); class Dog { constructor(id, name) { this.id = id; this.name = name; } }; class Cat { constructor(id, name, age) { this.id = id; this.name = name; this.age = age; } }; const DogType = new graphql.GraphQLObjectType({ name: 'Dog', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, } }); const CatType = new graphql.GraphQLObjectType({ name: 'Cat', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, age: { type: graphql.GraphQLInt }, } }); const PetType = new graphql.GraphQLUnionType({ name: 'Pet', types: [DogType, CatType], resolveType(value) { if (value instanceof Dog) { return DogType; } if (value instanceof Cat) { return CatType; } } }); let pets = { '1': new Dog('1', 'Jane'), '2': new Cat('1', 'Jane', 11), } const queryType = new graphql.GraphQLObjectType({ name: 'Query', fields: { pet: { type: PetType, args: { id: { type: graphql.GraphQLString } }, resolve: (_, { id }) => { return pets[id]; } } } }); const schema = new graphql.GraphQLSchema({ query: queryType }); const app = express(); app.use('/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, })); app.listen(3000, () => console.log('server started'));
В кода по-горе създадохме класовете Dog
и Cat
, които да служат като модели за нашите данни.
След това създаваме типовете GraphQL Dog
и Cat
, както следва:
const DogType = new graphql.GraphQLObjectType({ name: 'Dog', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, } }); const CatType = new graphql.GraphQLObjectType({ name: 'Cat', fields: { id: { type: graphql.GraphQLString }, name: { type: graphql.GraphQLString }, age: { type: graphql.GraphQLInt }, } });
Дефинирахме константите DogType
и CatType
, за да дефинираме типовете обекти Dog
и Cat
.
Dog
има id
и name
полета, а Cat
има id
, name
и age
полета.
След това дефинирахме типа обединение Pet
, което е обединение на Dog
и Cat
както следва:
const PetType = new graphql.GraphQLUnionType({ name: 'Pet', types: [DogType, CatType], resolveType(value) { if (value instanceof Dog) { return DogType; } if (value instanceof Cat) { return CatType; } } });
Имайте предвид, че имаме масив от types
и resolveType
метод вместо метода resolve
.
След това накрая създаваме нашия тип заявка, така че да можем да върнем отговор на потребителя, както следва:
const queryType = new graphql.GraphQLObjectType({ name: 'Query', fields: { pet: { type: PetType, args: { id: { type: graphql.GraphQLString } }, resolve: (_, { id }) => { return pets[id]; } } } });
Функцията resolve
получава записа pets
от id
и го връща, а ние посочихме, че type
, което връщаме, е PetType
.
След като направим това, можем да направим нашата заявка, използвайки вградени фрагменти, както следва:
{ pet(id: "1"){ __typename, ...on Dog { id name } ...on Cat { id name age } } }
В заявката по-горе направихме разлика между полетата на Dog
и Cat
с помощта на оператора ...on
. __typename
получава типа на върнатия обект.
С тази заявка трябва да получим:
{ "data": { "pet": { "__typename": "Dog", "id": "1", "name": "Jane" } } }
тъй като имаме Dog
екземпляр с ключ '1'
в pets
.
От друга страна, ако направим заявка за Pet
с ID 2, както следва:
{ pet(id: "2"){ __typename, ...on Dog { id name } ...on Cat { id name age } } }
Получаваме:
{ "data": { "pet": { "__typename": "Cat", "id": "1", "name": "Jane", "age": 11 } } }
тъй като имаме Cat
екземпляр като обект с ключ '2'
в pets
.
Заключение
Можем да създаваме типове с GraphQLObjectType
конструктор, за да създаваме типове обекти.
За да създадем обединени типове, можем да използваме GraphQLUnionType
, след което трябва да разрешим типа в метода resolveType
, като проверим типа на обекта и върнем правилния.
Можем да правим заявки за обединени типове с вградени фрагменти и да проверяваме типа с __typename
.