Как использовать GraphQL с Remix Framework

Как использовать GraphQL с Remix Framework

18 февраля 2023 г.

GraphQL — это современный и гибкий язык запросов данных, разработанный Facebook. Он представляет собой эффективную и мощную альтернативу традиционным REST API и становится все более популярным в последние годы. Remix Framework — это новый и инновационный подход к созданию приложений React, предлагающий ряд уникальных функций и преимуществ.

В этом сообщении блога мы рассмотрим процесс интеграции GraphQL с Remix Framework. Мы расскажем, как настроить проект, добавить к нему GraphQL API и создать запросы для получения данных. К концу этой статьи у вас будет четкое представление о том, как использовать GraphQL в приложении Remix React, и вы поймете, почему это отличный выбор для создания современных, масштабируемых и эффективных приложений React.

<цитата>

Обновление от 06 02 2023: я только что загрузил новый пост, в котором задается вопрос, почему вы вообще хотите использовать Remix и GraphQL вместе. Стоит прочитать!

Что такое GraphQL?

GraphQL — это язык запросов для API, разработанный Facebook в 2012 году. Он представляет собой более эффективную и гибкую альтернативу традиционным API REST. В REST API каждая конечная точка возвращает фиксированный набор данных, определенный дизайном API. Это может привести к избыточному или недостаточному выбору данных, а также к увеличению задержки, поскольку для получения всей необходимой информации может потребоваться несколько обращений к серверу.

GraphQL решает эти проблемы, позволяя клиенту точно указывать, какие данные ему нужны, и получать только эти данные в одном запросе. Это приводит к тому, что по сети передается меньше данных, сокращается задержка и повышается удобство для разработчиков, поскольку клиент может получить все необходимые данные одним запросом.

К другим преимуществам использования GraphQL относятся:

* Строгая типизация: GraphQL использует систему типов для определения структуры данных, возвращаемых с сервера, что упрощает обнаружение ошибок на ранних этапах процесса разработки. * Интерактивность: GraphQL предоставляет клиентам мощный способ изучения и тестирования возможностей API. * Сильное сообщество и инструменты: GraphQL имеет большое и постоянно растущее сообщество разработчиков, а также существует множество инструментов и библиотек с открытым исходным кодом для создания API GraphQL и работы с ними. * Универсальность: GraphQL можно использовать для получения данных из нескольких источников, включая базы данных, микросервисы и сторонние API.

Следовательно, преимущества GraphQL делают его лучшим выбором для создания надежных и эффективных API, пригодных для современного использования.

Что такое ремикс?

Remix Framework — это новый инновационный подход к созданию приложений React. Он предоставляет набор инструментов и библиотек, которые упрощают создание сложных приложений и управление ими, а также предлагает ряд уникальных функций, отличающих его от других платформ React.

Remix использует рендеринг на стороне сервера (SSR), мощную парадигму рендеринга, которая перемещает рендеринг содержимого страницы на серверную часть, сокращая снижение задержки страницы и узких мест на стороне клиента. Эта технология также позволяет Remix более эффективно управлять состоянием приложения.

Загрузчики и действия в Remix

В Remix Framework "Загрузчики" и "Действия" являются важными функциями, используемыми для управления состоянием и поведением вашего приложения.

Функция загрузчика предназначена для извлечения и хранения информации из внешних источников, таких как API. Эта функция может быть определена на вашем маршруте для быстрого доступа при ответе на запросы GET, чтобы получить данные, необходимые для рендеринга на стороне сервера.

Напротив, функции действий используются для управления поведением вашего приложения и могут использоваться для создания побочных эффектов, таких как отправка запроса API или переход к другой части приложения. Функции действий, назначенные на маршруте, будут отвечать в основном при вызове «модифицирующих» глаголов HTTP, таких как «POST», «PATCH» или «DELETE». Обычно эти функции используются для изменения состояния приложения или базовой структуры данных.

Загрузчики и действия работают вместе, чтобы обеспечить простой и удобный подход к организации состояния и функциональности вашего приложения. Кроме того, эти инструменты позволяют вам сосредоточиться на разработке базовой бизнес-логики вместо того, чтобы тратить время на поиск данных или волноваться о сложностях управления состоянием. Использование загрузчиков и действий упрощает создание сложных, но легко масштабируемых приложений!

Чем ремикс-маршруты отличаются от одностраничных маршрутов приложений

Ремикс-маршруты и маршруты одностраничных приложений (SPA) — это два разных подхода к маршрутизации в приложениях React.

В традиционном SPA маршруты управляются на стороне клиента и обычно определяются с помощью библиотеки, такой как React Router. Когда пользователь переходит к другой части приложения, маршрутизатор на стороне клиента обновляет URL-адрес и повторно отображает соответствующие компоненты. Это приводит к удобному и быстрому взаимодействию с пользователем, но также может привести к усложнению кода, поскольку необходимо управлять и поддерживать логику маршрутизации на стороне клиента.

Напротив, Remix App Routes управляется на стороне сервера и использует уникальный подход к маршрутизации, который отличается от традиционных маршрутов SPA. Когда пользователь переходит к другой части приложения, сервер создает новую HTML-страницу, а браузер переходит к новому URL-адресу. Это приводит к немного замедлению навигации, но дает ряд преимуществ, в том числе улучшение поисковой оптимизации, повышение производительности для новых пользователей и улучшение доступности для пользователей с медленным или ненадежным подключением к Интернету.

Почему это важно для приложения Remix GraphQL?

В традиционном SPA компонент React, которому нужны данные из конечной точки API GraphQL, будет использовать для извлечения данных что-то вроде клиента Apollo GraphQL. Компонент, скорее всего, определит хук "useQuery" для управления извлечением данных, когда компонент монтируется и отображается на стороне клиента.

Следующий код является примером классического приложения React, использующего клиент apollo для извлечения данных из GraphQL.

import React, { useEffect, useState } from 'react';
import { useQuery } from '@apollo/client';
import gql from 'graphql-tag';

const GET_USERS = gql`
  query {
    users {
      id
      name
      email
    }
  }
`;

const Users = () => {
  const { loading, error, data } = useQuery(GET_USERS);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    if (data) {
      setUsers(data.users);
    }
  }, [data]);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          {user.name} ({user.email})
        </li>
      ))}
    </ul>
  );
};

export default Users;

В отличие от традиционного React SPA, приложения Remix требуют делегирования извлечения данных и рендеринга страниц на стороне сервера. Хотя загрузка данных в клиенте является опцией для этого приложения, в Remix обычно оставляют эти задачи на усмотрение сервера, определяя загрузчики и действия на маршрутах приложений приложения.

Поскольку мы стремимся включить GraphQL в наше приложение для ремикса, это представляет собой сложный тест. Мы должны интегрировать GraphQL с функцией загрузчика или функцией действия в наши маршруты для успешного потока SSR при выполнении запросов GraphQL.

Настройка проекта Remix

В качестве отправной точки мы используем шаблон Blues Stack от Remix. Это дает нам преимущество перед множеством стандартного кода, включая приложение для создания заметок, а также схему Prisma и REST-подобные конечные точки. Однако, чтобы адаптировать его к нашим потребностям, мы изменим эти REST-подобные конечные точки в запросы GraphQL и изменим способ извлечения данных с помощью функций загрузки и действий.

Давайте создадим проект!

Создайте приложение для ремикса

Откройте терминал и введите следующую команду. Затем ответьте на вопросы, как показано в примере ниже.

❯ npx create-remix@latest --template remix-run/blues-stack
? Where would you like to create your app? remix-graphql
? TypeScript or JavaScript? TypeScript
? Do you want me to run `npm install`? Yes

Установить зависимости

Чтобы продолжить наше руководство, мы должны установить несколько зависимостей. В первую очередь потребуется клиент Apollo и некоторые инструменты — все это можно получить, запустив эту единственную команду из каталога вашего нового проекта.

❯ npm install @apollo/client @graphql-tools/schema @graphql-tools/utils

Добавление клиента Apollo в приложение Remix

Теперь мы начнем редактировать код! Начнем с добавления схемы GraphQL в наше приложение.

Определить схему и запросы

Для начала создайте каталог под названием "graphql" в папке app. Затем добавьте файл с именем schema.server.ts, в котором будут определены все ваши запросы и схемы GraphQL!

<цитата>

Суффикс .server.ts действует как подсказка для проекта Remix, указывая на то, что он должен исключить этот файл во время связывания кода на стороне клиента, тем самым гарантируя, что эти файлы будут работать исключительно на стороне сервера. вашего приложения.

// ~/graphql/schema.server.ts
import { gql } from '@apollo/client';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { resolvers } from './resolvers.server';

const typeDefs = gql`
  type Note {
    id: String
    title: String
    body: String
    createdAt: String
    updatedAt: String
    userId: String
  }

  type Query {
    notes(userId: String!): [Note]
  }
`;

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

Чтобы получить доступ к заметкам пользователя, мы объявили тип схемы GraphQL Note и создали запрос notes, для которого требуется userId. р>

Определить преобразователи

Далее мы должны создать дополнительный файл в каталоге "graphql" под названием resolvers.server.ts. Здесь будут установлены наши функции распознавания.

// ~/graphql/resolver.server.ts
import type { IResolvers } from '@graphql-tools/utils';
import { prisma } from '../db.server';

export const resolvers: IResolvers = {
  Query: {
    notes: async (_: any, { userId }: { userId: string }) => {
      return prisma.note.findMany({
        where: { userId },
        orderBy: { updatedAt: 'desc' },
      });
    },
  },
};

Здесь определяется функция разрешения notes. Он будет выполняться всякий раз, когда мы делаем вызов запроса notes. Если вы посмотрите на существующий ~models/note.server.ts, вы заметите, что этот код очень похож на код функции getNoteListItems. Это не ошибка, так как все, что нам нужно было сделать, это переместить его сюда и удалить весь ненужный код, такой как параметр "select", поскольку все должно вернуться (GraphQL решает, какие поля должны быть возвращены).

Создайте клиент Apollo

Теперь мы подошли к файлу client.server.ts, который отвечает за сборку всех наших компонентов вместе и определение экземпляра клиента Apollo с системой Schema Link из пакета Apollo Client.< /p>

// ~/graphql/client.server.ts
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { SchemaLink } from '@apollo/client/link/schema';
import { schema } from './schema.server';

export const client = new ApolloClient({
  cache: new InMemoryCache(),
  ssrMode: true,
  link: new SchemaLink({ schema }),
});

Благодаря созданному нами клиенту наши запросы теперь могут успешно выполняться на стороне сервера.

Используйте клиент для получения данных из серверного API

Далее давайте изменим вызов API, чтобы включить запросы GraphQL, которые мы разработали ранее.

// ~/models/note.server.ts
...
export function getNoteListItems({ userId }: { userId: User["id"] }) {
  return client.query<{ data: { notes: { id: string; title: string }[] } }>({
    query: gql`
      query getNotes($userId: String!) {
        notes(userId: $userId) {
          id
          title
        }
      }
    `,
    variables: { userId },
  });
}
...

Показать данные

Наконец, мы изменим маршрут notes.tsx, чтобы эффективно использовать полученные данные.

// ~/routes/notes.tsx
import type { LoaderArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import { Form, Link, NavLink, Outlet, useLoaderData } from '@remix-run/react';

import { getNoteListItems } from '~/models/note.server';
import { requireUserId } from '~/session.server';
import { useUser } from '~/utils';

export async function loader({ request }: LoaderArgs) {
  const userId = await requireUserId(request);
  const noteListItems = await getNoteListItems({ userId });
  return json({ noteListItems });
}

export default function NotesPage() {
  const data = useLoaderData<typeof loader>();
  const user = useUser();

  return (
    <div className="flex h-full min-h-screen flex-col">
      <header className="flex items-center justify-between bg-slate-800 p-4 text-white">
        <h1 className="text-3xl font-bold">
          <Link to=".">Notes</Link>
        </h1>
        <p>{user.email}</p>
        <Form action="/logout" method="post">
          <button
            type="submit"
            className="rounded bg-slate-600 py-2 px-4 text-blue-100 hover:bg-blue-500 active:bg-blue-600"
          >
            Logout
          </button>
        </Form>
      </header>

      <main className="flex h-full bg-white">
        <div className="h-full w-80 border-r bg-gray-50">
          <Link to="new" className="block p-4 text-xl text-blue-500">
            + New Note
          </Link>

          <hr />

          {data.noteListItems.data.notes.length === 0 ? (
            <p className="p-4">No notes yet</p>
          ) : (
            <ol>
              {data.noteListItems.data.notes.map((note) => (
                <li key={note.id}>
                  <NavLink
                    className={({ isActive }) =>
                      `block border-b p-4 text-xl ${isActive ? 'bg-white' : ''}`
                    }
                    to={note.id}
                  >
                    📝 {note.title}
                  </NavLink>
                </li>
              ))}
            </ol>
          )}
        </div>

        <div className="flex-1 p-6">
          <Outlet />
        </div>
      </main>
    </div>
  );
}

Нет необходимости изменять функцию загрузчика страниц заметок! Нам нужно было лишь внести небольшие изменения в логику доступа к данным из-за небольшого изменения в структуре данных.

Куда двигаться дальше

Это было только начало. Теперь мы модифицировали Remix для использования GraphQL с одним запросом GraphQL. Вы также можете расширить это, добавив больше запросов для отдельных Note и мутаций, чтобы создавать или редактировать их по мере необходимости. Кроме того, вы также можете поэкспериментировать с входом и выходом с помощью запросов и мутаций GraphQL, если хотите! Еще многое предстоит сделать, но это должно послужить отличной отправной точкой.

Я бы также предложил следующие ресурсы для получения дополнительной информации об интеграции GraphQL/Remix. Я нашел их очень полезными.


Также опубликовано здесь


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE