Разрешаване на вложени заявки в GraphQL

Първоначално публикувано на https://www.wisdomgeek.com на 9 декември 2020 г.

Когато създаваме GraphQL сървър с релационни данни, искаме да върнем данните в йерархичен формат с тези връзки в една заявка. В края на краищата, това е мястото, където GraphQL е полезен, нали? Нека разгледаме как можем да направим това с помощта на вложени заявки в GraphQL.

Тази публикация предполага, че имате познания за GraphQL заявки и как да създадете GraphQL сървър с помощта на Apollo Server. Можете да препратите към тези публикации, ако сте нов в GraphQL.

Нека приемем, че имаме приложение за блогове, в което имаме потребители и публикации. Типовете за тях са определени като:

type Post {
    id: ID!
    title: String!
    authorId: ID!
  }

  type Author {
    id: ID!
    name: String!
    posts: [Post]
  }

Добавяне на примерни твърдо кодирани данни за нашето приложение,

const posts = [
  {
    id: 1,
    title: 'Why GraphQL?',
    authorId: 1,
  },
  {
    id: 2,
    title: 'Creating a GraphQL API with Apollo Server',
    authorId: 1,
  },
  {
    id: 3,
    title: 'This should not be returned',
    authorId: 2,
  },
];

const authors = [{ id: 1, name: 'Saransh Kataria' }];

Сега, когато направихме първоначалната си настройка. Нека да видим какво очакваме от нашето запитване:

Очакваният резултат

За твърдо кодираните стойности по-горе искаме да отправим запитване към всички потребители и да получим съответните им публикации. Така нашата заявка ще изглежда така:

query {
  authors {
    id,
    name,
    posts {
      title
    }
  }
}

Публикациите са свързани с автор, ние търсим авторите и след това подзапитваме техните публикации. Тъй като публикациите не са скаларен тип, а потребителски тип, трябва да посочим кои от техните свойства трябва да бъдат извлечени.

И очакваме резултатът за нашия случай на употреба да бъде:

{
  "data": {
    "authors": [
      {
        "id": "1",
        "name": "Saransh Kataria",
        "posts": [
          {
            "title": "Why GraphQL?"
          },
          {
            "title": "Creating a GraphQL API with Apollo Server"
          }
        ]
      }
    ]
  }
}

Създаване на заявка и резолвер за авторите

Без да се притесняваме за полето за публикации, можем да настроим резолвера за заявката за автори. Това ще бъде:

const resolvers = {
  Query: {
    authors: () => {
      return authors;
    },
  },
}

Връщаме масива автори, когато получим заявката за автори. Това е доста просто. Обектът вече има всички съответни свойства, от които се нуждае типът, те се разрешават и ние получаваме очаквания отговор.

Но за нашето релационно поле, публикации, ние нямаме това. Можем да определим връзката на автора само от масива с публикации.

Разрешаване на вложени заявки в GraphQL

Когато настройваме поле, чиято стойност е персонализиран тип, трябва да дефинираме функция, която казва на GraphQL как да получи този персонализиран тип. В нашия случай искаме да кажем на GraphQL как да получава публикациите, ако имаме автора. Ние правим това, като дефинираме ново коренно свойство вътре в резолверите.

Наред със заявката ще добавим ново свойство, което ще каже на GraphQL как да разреши автор. Свойството автор ще бъде обект и ние ще създадем метод за всяко от полетата, които трябва да бъдат разрешени.

Apollo Server може автоматично да разрешава скаларните свойства. Трябва само да създадем резолвер за вложени свойства. За разрешаване на вложени заявки в GraphQL, ние създаваме само метод за свойствата, които препращат към други персонализирани типове. В нашия случай имаме само полето за публикации, което търсим. Така че ще дефинираме това като:

const resolvers = {
  Query: {... },
  Author: {
    posts: () => {...},
  },
}

Тъй като posts е метод за преобразуване точно като другите методи в сървъра на Apollo, получаваме достъп до всичките 4 параметъра, които получаваме в други методи. Целта на този метод е да върне правилните публикации, съответстващи на автора. За да направим това, имаме нужда от информация от обекта на автора, като идентификатора на автора.

Заявката за публикации се извиква от авторския резолвер, родителят за резолвера на публикации ще сочи към текущия автор. По този начин, за да разрешим тази вложена заявка, ще използваме този параметър, който й е предаден. Можем да го наречем родител, но вече знаем, че ще бъде автор. По този начин можем да назовем параметъра като автор.

Използвайки автора, можем лесно да разберем кои публикации трябва да бъдат върнати.

const resolvers = {
  Query: {
    authors: () => {
      return authors;
    },
  },
  Author: {
    posts: (author) => {
      return posts.filter((post) => post.authorId === author.id);
    },
  },
};

Краен код

const { gql, ApolloServer } = require('apollo-server');

const posts = [
  {
    id: 1,
    title: 'Why GraphQL?',
    authorId: 1,
  },
  {
    id: 2,
    title: 'Creating a GraphQL API with Apollo Server',
    authorId: 1,
  },
  {
    id: 3,
    title: 'This should not be returned',
    authorId: 2,
  },
];

const authors = [{ id: 1, name: 'Saransh Kataria' }];

const typeDefs = gql`
  type Post {
    id: ID!
    title: String!
    authorId: ID!
  }

  type Author {
    id: ID!
    name: String!
    posts: [Post]
  }

  type Query {
    authors: [Author]
  }
`;

const resolvers = {
  Query: {
    authors: () => {
      return authors;
    },
  },
  Author: {
    posts: (author) => {
      return posts.filter((post) => post.authorId === author.id);
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen(4000).then(({ url }) => {
  console.log(`Server started at ${url}`);
});

Заключение

Когато стартираме заявката сега, получаваме желания резултат:

Първата функция, която трябва да бъде извикана, е функцията за разрешаване на автори, тъй като това е, което иска заявката. Връща идентификатора, името и заглавието за всички автори. В нашия случай имаме само един и той се връща.

След това GraphQL проверява какви данни са били поискани. Ако бяха поискани само име и идентификатор, изпълнението на функцията ще приключи дотук, тъй като това са скаларни типове.

Но ние поискахме публикации. И публикации не съществуват в обекта на авторите. Така че GraphQL ще извика функцията posts за всеки отделен автор. И това е мястото, където нашето разрешаване на вложени заявки в GraphQL влиза в картината. Нашата функция за преобразуване на публикации се извиква с родителския набор като автор ({ id: 1, име: ‘Saransh Kataria’ }).

Ако в нашия твърдо кодиран пример присъстват множество автори, функцията за публикации ще бъде извикана индивидуално и за двамата. Ние използваме идентификатора на предадения родителски автор, за да извлечем публикациите на автора и да ги върнем.

Можем да дефинираме връзката по различен начин, като дефинираме публикации в типа автор и след това създадем вложена GraphQL заявка по този начин. Дефиницията на схемата, декларацията и нейното изпълнение зависят от нас. И ние можем да разрешим вложени заявки в GraphQL, както искаме, след като знаем как да го направим.

Ако имате някакви въпроси, не се колебайте да оставите коментар по-долу.