Amplication & React: Использование GraphQL

Добро пожаловать в этот учебник по созданию полнофункционального приложения с помощью Amplication.

Мы будем шаг за шагом создавать приложение Todos, используя React для фронтенда и Amplication для бэкенда.

Если вы застряли, у вас возникли вопросы или вы просто хотите поздороваться с другими разработчиками Amplication, такими же как вы, то присоединяйтесь к нашему Discord!


Оглавление

  • Шаг 1 — GraphQL
  • Шаг 2 — Обновление lib/auth.js
  • Шаг 3 — Обновление lib/tasks.js
  • Шаг 4 — Подведение итогов

Шаг 1 — GraphQL

До сих пор мы взаимодействовали с бэкендом Amplication посредством HTTP-запросов. Однако Amplication предоставляет другой способ взаимодействия с бэкендом — GraphQL. GraphQL — это язык запросов, который позволяет создавать читаемые команды с множеством преимуществ. Если вы хотите узнать больше о том, почему GraphQL может быть лучшим выбором для вашего приложения, я рекомендую прочитать эту статью команды Apollo.

Если вы запустили бэкенд (npm run start:backend), вы можете поработать с запросами на GraphQL Playground http://localhost:3000/graphql.

Для выполнения GraphQL-запросов к бэкенду мы будем использовать библиотеку @apollo/client. Сначала установите @apollo/client как зависимость в подпапку web:

cd web
npm install @apollo/client
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы захотим настроить наш GraphQL клиент. Создайте следующий файл web/src/lib/apollo.js и в верхней части файла импортируйте @apollo/client.

Затем вставьте следующий код:

import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

const apiUrl = "http://localhost:3000/graphql";
const jwtKey = "accessToken";

const httpLink = createHttpLink({
   uri: apiUrl,
});

const authLink = setContext((_, { headers }) => {
   const token = localStorage.getItem(jwtKey);
   return {
      headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
      },
   };
});

export const client = new ApolloClient({
   link: authLink.concat(httpLink),
   cache: new InMemoryCache(),
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Как и в шаге 4 учебника, @apollo/client был настроен на получение токена доступа JWT пользователя и присвоение его заголовку Authorization каждого запроса.

Мы также хотим включить функции для проверки существования маркера доступа и для сохранения нового маркера доступа.

export const isStoredJwt = () => Boolean(localStorage.getItem(jwtKey));
export const setStoredJwt = (accessToken) =>
   localStorage.setItem(jwtKey, accessToken);
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, мы захотим экспортировать gql из @apollo/client. Это позволит писать GraphQL-запросы и мутации.

export { gql } from "@apollo/client";
Вход в полноэкранный режим Выход из полноэкранного режима

Шаг 2 — Обновление lib/auth.js

Откройте web/src/lib/auth.js и удалите весь код в файле. В верхней части файла мы импортируем некоторые функции, которые мы создали в файле web/src/lib/apollo.js.

import { gql, isStoredJwt, setStoredJwt, client } from "./apollo";
Вход в полноэкранный режим Выход из полноэкранного режима

Во-первых, добавьте новую функцию me:

const GET_ME = gql`
   query me {
      me {
      id
      }
   }
`;

export const me = async () => {
   return isStoredJwt()
      ? (await client.query({ query: GET_ME }).catch(() => null))?.data.me
      : null;
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Вы заметите, что запрос к учетной записи пользователя разбит на две части: GET_ME и me. Первая переменная, GET_ME — это место, где записывается запрос. Одно из преимуществ GraphQL заключается в том, что мы сообщаем бэкенду, какие данные нам нужны. В данном случае нам нужен только id пользователя, так что это все, что запрашивает данный запрос.

me будет фактически выполнять запрос.

Далее добавьте функцию login:

const LOGIN = gql`
   mutation login($credentials: Credentials!) {
      login(credentials: $credentials) {
      accessToken
      }
   }
`;

export const login = async (username, password) => {
   const result = (
      await client
      .mutate({
         mutation: LOGIN,
         variables: { credentials: { username, password } },
      })
      .catch(() => null)
   )?.data.login;

   if (!result) {
      return alert("Could not login");
   }
   setStoredJwt(result.accessToken);
   return me();
};
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, вместо того чтобы называть это запросом, мы будем называть эту функцию мутацией. Запросы используются для чтения данных, а мутации — для записи данных. Вход в систему и регистрация технически являются записью данных, поскольку в бэкенде создается сессия.

LOGIN — это мутация, которая принимает имя пользователя и пароль пользователя как объект и возвращает только accessToken из запроса.

login будет выполнять мутацию подобно реализации HTTP. Вместо того чтобы передавать учетные данные в BODY HTTP-запроса, учетные данные (и другие аргументы в целом) передаются в объекте variables. Ключевые значения variables отображаются на имена переменных в mutation, которые мы пишем. Так, variables.credentials в client.mutate отображается на $credentials в mutation login($credentials: Credentials!).

Наконец, добавьте функцию signup:

const SIGNUP = gql`
   mutation signup($credentials: Credentials!) {
      signup(credentials: $credentials) {
      accessToken
      }
   }
`;

export const signup = async (username, password) => {
   const result = (
      await client
      .mutate({
         mutation: SIGNUP,
         variables: { credentials: { username, password } },
      })
      .catch(() => null)
   )?.data.signup;

   if (!result) {
      return alert("Could not sign up");
   }
   setStoredJwt(result.accessToken);
   return me();
};
Вход в полноэкранный режим Выход из полноэкранного режима

Шаг 3 — Обновление lib/tasks.js

Далее нам нужно будет обновить функции задач для использования GraphQL. Откройте web/src/lib/tasks.js, удалите весь код в файле и замените его следующим:

import { gql, client } from "./apollo";

const CREATE_TASK = gql`
   mutation createTask($data: TaskCreateInput!) {
      createTask(data: $data) {
      completed
      createdAt
      id
      text
      }
   }
`;

export const create = async (text, uid) => {
   const result = (
      await client
      .mutate({
         mutation: CREATE_TASK,
         variables: {
            data: {
            completed: false,
            text,
            uid: { id: uid },
            },
         },
      })
      .catch(() => null)
   )?.data.createTask;

   if (!result) {
      return alert("Could not create task");
   }

   return result;
};

const GET_TASKS = gql`
   query tasks($where: TaskWhereInput, $orderBy: [TaskOrderByInput!]) {
      tasks(where: $where, orderBy: $orderBy) {
      completed
      createdAt
      id
      text
   }
   }
`;

export const getAll = async (uid) => {
   const result = (
      await client
      .query({
         query: GET_TASKS,
         variables: {
            where: { uid: { id: uid } },
            orderBy: { createdAt: "Asc" },
         },
      })
      .catch(() => null)
   )?.data.tasks;

   if (!result) {
      alert("Could not get tasks");
      return [];
   }

   return result;
};

const UPDATE_TASK = gql`
   mutation updateTask($data: TaskUpdateInput!, $where: TaskWhereUniqueInput!) {
      updateTask(data: $data, where: $where) {
      completed
      createdAt
      id
      text
      }
   }
`;

export const update = async (task) => {
   const result = (
      await client
      .mutate({
         mutation: UPDATE_TASK,
         variables: {
            data: {
            completed: !task.completed,
            },
            where: {
            id: task.id,
            },
         },
      })
      .catch(() => null)
   )?.data.updateTask;

   if (!result) {
      return alert("Could not update task");
   }

   return result;
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Шаг 4 — Завершение

Запустите приложение и поиграйте!

Задачи пользователей теперь сохраняются в бэкенде Amplication с помощью GraphQL-запросов и мутаций, а не традиционных HTTP-запросов.

Поздравляем разработчика. Возьмите с собой то, чему вы научились, и создайте что-то удивительное.

Если вам нужна помощь или вы хотите поделиться своими наработками, присоединяйтесь к нашему Discord.

Чтобы просмотреть изменения для этого шага, посетите здесь.

Оцените статью
Procodings.ru
Добавить комментарий