Інтеграція KeystoneJS з фронтендом через GraphQL API

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Інтеграція KeystoneJS з фронтендом через GraphQL API
Середня
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Інтеграція KeystoneJS з фронтендом через GraphQL API

KeystoneJS автоматично генерує повнофункціональний GraphQL API з описання Lists. Для кожного List створюються запити (query/queries/count), мутації (create/update/delete) та типи фільтрів. Завдання інтеграції — правильно організувати клієнтську частину.

Що генерується автоматично

Для List Post KeystoneJS створює:

  • post(where: PostWhereUniqueInput!): Post
  • posts(where: PostWhereInput, orderBy: [...], take: Int, skip: Int): [Post!]
  • postsCount(where: PostWhereInput): Int
  • createPost(data: PostCreateInput!): Post
  • createPosts(data: [PostCreateInput!]!): [Post]
  • updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post
  • deletePost(where: PostWhereUniqueInput!): Post

Налаштування Apollo Client

// lib/apollo.ts
import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';

const httpLink = createHttpLink({
  uri: process.env.NEXT_PUBLIC_KEYSTONE_URL + '/api/graphql',
  credentials: 'include', // для cookie-сесій
});

const authLink = setContext((_, { headers }) => ({
  headers: {
    ...headers,
    // Якщо використовується JWT замість cookie
    authorization: getToken() ? `Bearer ${getToken()}` : '',
  },
}));

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, extensions }) => {
      if (extensions?.code === 'UNAUTHENTICATED') {
        window.location.href = '/login';
      }
    });
  }
});

export const apolloClient = new ApolloClient({
  link: from([errorLink, authLink, httpLink]),
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          posts: {
            keyArgs: ['where', 'orderBy'],
            merge(existing = [], incoming) {
              return [...existing, ...incoming];
            },
          },
        },
      },
    },
  }),
});

Типізовані запити з codegen

npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
# codegen.yml
schema: http://localhost:3000/api/graphql
documents: src/**/*.graphql
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
# src/queries/posts.graphql
query GetPosts($where: PostWhereInput, $take: Int, $skip: Int) {
  posts(where: $where, take: $take, skip: $skip, orderBy: [{ publishedAt: desc }]) {
    id
    title
    slug
    publishedAt
    status
    author {
      id
      name
    }
    tags {
      id
      name
    }
  }
  postsCount(where: $where)
}

mutation CreatePost($data: PostCreateInput!) {
  createPost(data: $data) {
    id
    slug
  }
}

Використання в Next.js (Server Components)

// app/blog/page.tsx
import { getClient } from '@/lib/apollo-server';
import { GetPostsDocument } from '@/generated/graphql';

export default async function BlogPage({ searchParams }) {
  const page = Number(searchParams.page) || 1;
  const { data } = await getClient().query({
    query: GetPostsDocument,
    variables: {
      where: { status: { equals: 'published' } },
      take: 10,
      skip: (page - 1) * 10,
    },
  });

  return <PostGrid posts={data.posts} total={data.postsCount} page={page} />;
}

Мутації в Client Components

'use client';
import { useMutation } from '@apollo/client';
import { CreatePostDocument } from '@/generated/graphql';

export function NewPostForm() {
  const [createPost, { loading, error }] = useMutation(CreatePostDocument, {
    update(cache, { data }) {
      // Інвалідуємо кеш списку постів
      cache.evict({ fieldName: 'posts' });
    },
  });

  const handleSubmit = async (formData: PostFormData) => {
    const { data } = await createPost({
      variables: {
        data: {
          title: formData.title,
          slug: formData.slug,
          content: { document: formData.content },
          author: { connect: { id: currentUserId } },
          status: 'draft',
        },
      },
    });
    router.push(`/admin/posts/${data?.createPost?.id}`);
  };
}

Аутентифікація через GraphQL

KeystoneJS створює мутації аутентифікації автоматично:

const LOGIN = gql`
  mutation Login($email: String!, $password: String!) {
    authenticateUserWithPassword(email: $email, password: $password) {
      ... on UserAuthenticationWithPasswordSuccess {
        sessionToken
        item { id name email role }
      }
      ... on UserAuthenticationWithPasswordFailure {
        message
      }
    }
  }
`;

const WHOAMI = gql`
  query WhoAmI {
    authenticatedItem {
      ... on User { id name email role }
    }
  }
`;

Пагінація та infinite scroll

const { data, fetchMore, loading } = useQuery(GetPostsDocument, {
  variables: { take: 10, skip: 0 },
});

const loadMore = () => {
  fetchMore({
    variables: { skip: data.posts.length },
    updateQuery: (prev, { fetchMoreResult }) => ({
      postsCount: fetchMoreResult.postsCount,
      posts: [...prev.posts, ...fetchMoreResult.posts],
    }),
  });
};

Типична інтеграція Next.js + KeystoneJS GraphQL з codegen, аутентифікацією та базовими CRUD-операціями — 3–5 днів.