Можем да създадем прост 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.