Apollo

Apollo

Apollo is a GraphQL client and cache for JavaScript, iOS (Swift) and Android.
Enoch Barcenas

Enoch Barcenas

1682499852

How to Manage Local State with Apollo Client

In this tutorial, you will learn how to use reactive variables and field policies in GraphQL's Apollo Client to manage local state in client-side, JavaScript framework apps.

State management is a critical aspect of building modern web applications. It involves the storage, retrieval, and modification of data that represents the state of the application. As applications grow in complexity, it’s even more important to have a straightforward, simplified approach for managing state in a centralized and consistent way.

This is where GraphQL’s Apollo Client comes in, providing a solution for state management in client-side applications. According to the docs, Apollo Client enables the management of local state alongside remotely fetched state, meaning we can interact with all of our application’s state with a single API.

In this article, we‘ll discuss how to manage local state using two features introduced in Apollo Client 3: reactive variables and field policies.

Table of contents:

  • What are reactive variables?
  • Creating a reactive variable
  • Using reactive variables to read local data
  • Using field policies and local-only fields to read local data
  • Using reactive variables to modify local data
  • Reacting to reactive variable data changes with useReactiveVar

What are reactive variables?

Reactive variables provide a flexible and powerful tool for managing local state within a JavaScript framework application. By utilizing reactive variables, we can read and modify local data anywhere within our application, without the need for a GraphQL operation. This allows for greater freedom and control over how we manage our application’s data and state.

It is important to note that reactive variables are distinct from the data stored within the Apollo Client cache. Since reactive variables are not required to follow a strict data structure, we can store virtually any type of data in them.

One of the key benefits of reactive variables is their automatic detection of changes using the useReactiveVar hook. When the value of a reactive variable is updated, Apollo Client is able to detect that change and automatically trigger updates to any active queries that depend on that variable. This allows for seamless, real-time updates to our app’s UI, without the need for manual intervention.

Reactive variables provide a useful mechanism for managing local state in client-side applications and are an important aspect of Apollo Client’s state management capabilities.

Let’s dive into the syntax so that we can better understand how to create, read, modify, and react to reactive variables in Apollo Client.

Creating a reactive variable

Apollo Client provides us with a makeVar() method that we can use to create a reactive variable. All we have to do is import it into our code:

// JavaScript
import { makeVar } from '@apollo/client';

const name = makeVar("Mark Andrews");

N.B., for the examples used in this article, we’re using a sample React application

The makeVar method takes a single argument, which is the initial value of the reactive variable. In this case, the initial value of the reactive variable is the string "Mark Andrews".

Once the reactive variable is created, it can be used in our application to read and modify its value.

Using reactive variables to read local data

To read the current value of a reactive variable, simply call the function that was returned by the makeVar method without any arguments:

// JavaScript
const name = makeVar("Mark Andrews")

console.log(name());

// Output: Mark Andrews

After the reactive variable is created, the value of the name variable can be accessed by invoking the variable as a function.

The value of a reactive variable can be read at any time, and it will always return the current value of the variable. In this case, the value of name will be logged to the console as Mark Andrews.

It is worth noting that reactive variables in Apollo Client are updated automatically, so even if the value of the name variable is changed elsewhere in the code, the logged value will always reflect the latest value of the variable.

However, there is a way to modify a reactive variable in Apollo Client, as we’ll see a little later in this article.

Using field policies and local-only fields to read local data

We can also read data from a reactive variable by using the GraphQL useQuery syntax the same way we would fetch remote data. The only difference is that we place the @client directive after the field name to specify that Apollo must resolve the query on the client.

This works by using the cache type and field policies to define how a specific field in our Apollo Client cache is read and written to.

To better understand this, let’s look at an example:

import { InMemoryCache } from '@apollo/client';
import { name } from './filename';

export default new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        name: {
          read() {
            return name();
          }
        }
      }
    }
  }
})

In the above code, we’re exporting a new instance of the InMemoryCache class with a custom typePolicies object. The typePolicies object is used to define how the cache should handle certain types of data returned by GraphQL queries. In this case, the Query type is defined with a name field.

We use the read function to specify how the name field should be read from the cache. In this case, it calls the imported name function to retrieve the value.

Essentially, we’re defining a custom cache configuration for the Apollo Client to use when making GraphQL queries.

Now, let’s write a query for our name reactive variable, like we would for remote data, using the useQuery syntax:

import { gql } from "@apollo/client";

export const NAME_REACTIVE_VARIABLE = gql`
  query getNameReactiveVariable{
    name @client
  }
`

Our query, getNameReactiveVariable, has only one field, name, which is annotated with @client. The @client directive is used to indicate that this field should be resolved on the client side, rather than being sent to the server.

Now, we can use the query wherever we’d like in our application:

import { useQuery } from '@apollo/client';
import { NAME_REACTIVE_VARIABLE } from './filename';

const {loading, error, data} = useQuery(NAME_REACTIVE_VARIABLE);

Using reactive variables to modify local data

To modify the current value of a reactive variable, simply call the function that was returned by the makeVar method with a single argument which is the new value:

const name = makeVar("Mark Andrews");

console.log(name("John Doe"))
// Output: John Doe

The value of the name variable is updated by invoking the variable as a function and passing in the new value as an argument. In the above code, the value of the name variable is changed from "``Mark Evans``" to "John Doe".

It’s important to note that once the value of a reactive variable is updated, every active query with a field that depends on the changed variable will automatically update. This means that changing the name variable will immediately impact other parts of the application that rely on the value of this variable.

It is also worth noting that we should not mutate an existing object or array, because in doing so, we may unintentionally affect other parts of the code that rely on that same object or array. This can lead to bugs that are difficult to track down. Instead, we should pass a new copy, like so:

const originalObject = makeVar({ key1: 'value1', key2: 'value2' });

let newObject = originalObject({ ...originalObject, key3: 'value3', key4: 'value4' });

console.log(newObject());
// Output:{ key1: 'value1', key2: 'value2', key3: 'value3', key4: 'value4' }

Reacting to reactive variable data changes with useReactiveVar

Reactive variables have the ability to cause changes in our application that are also reactive. Whenever we make a change to a reactive variable, any related queries will be refreshed and our application’s user interface will update accordingly. The useReactiveVar hook allows React components to directly include the values of reactive variables in their state, without the need to wrap them in a query.

With the useReactiveVar hook, we can access the value of a reactive variable in a manner that enables re-rendering of the React component whenever the variable is modified in the future:

import { makeVar, useReactiveVar } from "@apollo/client";
export const name = makeVar("Mark Andrews");

// we use the reactive variable in our component like so:

export function Profile() {
  const username = useReactiveVar(name);
}

Prior to the availability of the useReactiveVar hook, we could only access the value of a reactive variable by using the useQuery method.

Conclusion

Reactive variables and field policies are essential aspects of Apollo Client’s state management capabilities for managing local state in client-side, JavaScript framework applications.

In this article, we explored how to create reactive variables in GraphQL’s Apollo Client and use them to read and modify data and react to data changes.

Apollo Client reactive variables are an essential tool for any developer looking to build modern, data-driven web applications. With their powerful capabilities and flexible programming model, they provide a powerful solution for managing complex state on the client side, and are sure to play a central role in the future of web development.

Source: https://blog.logrocket.com

#apollo #graphql #javascript 

How to Manage Local State with Apollo Client

Создайте приложение CRUD с помощью React 18.2, Apollo GraphQL 4

В этом руководстве мы создадим приложение CRUD с помощью React 18.2 и Apollo GraphqL 4. Это руководство предназначено для разработчиков React, которые хотят погрузиться в React graphQL . Вам нужно практическое знание React, чтобы следовать.

Условие:

Чтобы выполнить этот урок наиболее эффективным способом, вам нужно помнить о некоторых вещах.

  • Настроенный компьютер с подключением к Интернету.
  • Базовые знания React, HTML и CSS.
  • Текстовый редактор. Вы можете выбрать любой текстовый редактор, в котором вы можете писать код JavaScript, например возвышенный текст, nodepad++ и т. д. В этом руководстве мы будем использовать код Visual Studio.
  • NodeJS установлен на вашем компьютере.

Краткий обзор GraphQL

  • GraphQL — это язык запросов данных и манипулирования ими с открытым исходным кодом для интерфейсов прикладного программирования (API).
  • GraphQL, в отличие от других форматов API, позволяет потребителю API запрашивать определенные данные с сервера приложения, не получая ненужной информации.
  • Напротив, традиционные архитектуры REST API предоставляют фиксированную структуру данных, а затем полагаются на клиентов для фильтрации любой ненужной информации.
  • Дизайн GraphQL обеспечивает преимущество с точки зрения безопасности. Поскольку GraphQL не возвращает данные, которые клиент явно не запросил, его использование снижает риск проблем с раскрытием информации.
  • GraphQL не зависит от языка и работает с широким спектром языков программирования, включая JavaScript, Java, Python и Ruby.
  • GraphQL реализует схему для определения типов данных, которые можно запрашивать, в дополнение к отношениям между этими типами.

Давайте посмотрим на пример. Допустим, вашему приложению необходимо получить данные пользователя с сервера. При использовании REST ваш ответ может выглядеть так:

{
  user_details: {
    id: 2348,
    name: “Deven Rathore”,
    email: “deven@example.com”
  }
}

С GraphQL вы можете запрашивать именно те данные, которые вам нужны, и быть уверенными, что получите только то, что просили. Если предположить, что вам нужно просто имя пользователя  , ваш ответ будет выглядеть так:

{
  user_details: {
    name: “Deven Rathore”
  }
}

С GraphQL вы получаете только одну конечную точку, на которую вы отправляете  запросы  и  мутации  для управления вашими данными.

Краткий обзор Аполлона:

Apollo  — это набор инструментов и библиотек, разработанных (Meteor Development Group), чтобы помочь разработчикам использовать GraphQL в своих проектах. У Apollo есть клиент и сервер. 

Сервер Аполлона:

Сервер Apollo — это библиотека, созданная для упрощения подключения схемы GraphQL к серверу HTTP (протокол передачи гипертекста) в Node.js и поддерживаемых им платформах. 

Клиент Аполлона:

Клиент Apollo — это библиотека, созданная для упрощения получения данных с серверов GraphQL с помощью JavaScript.

Это полнофункциональный кэширующий клиент GraphQL. Его можно использовать с любой современной средой JavaScript, такой как Vue.js, React, Angular и другими.

Прежде чем начать:

Поднимите свои навыки веб-разработки на новый уровень с помощью комплексного курса React, предлагаемого Pluralsight . Эта ведущая в отрасли учебная платформа предлагает уроки под руководством экспертов, которые охватывают новейшие методы и лучшие практики в React. Начните учиться прямо сейчас и наблюдайте, как ваша уверенность и способности растут с каждым уроком.

Для этого руководства у нас будет два работающих сервера: один обслуживает наш бэкенд, а второй обслуживает наше приложение React CRUD. Сначала мы начнем с бэкэнда.

Настройка сервера Аполлона

В терминале создайте новый каталог и установите необходимые пакеты:

mkdir todo-graphql && cd todo-graphql

Внутри этого каталога создайте еще одну папку:

mkdir server

сервер будет содержать наш сервер GraphQL. Перейдите в папку сервера в вашем терминале:

cd server

Теперь установите следующее:

npm install @apollo/server graphql

Создайте файл в корневом каталоге с именем  index.js и добавьте следующее:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

let todos = [
    {
      id: Date.now().toString(),
      text: 'Hello from GraphQL',
      completed: true,
    },
  ];

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

Мы будем хранить задачи в массиве, но массив можно заменить базой данных или файлом. Для простоты мы будем использовать массив.

У каждой задачи есть идентификатор, текст и завершение. Все идет нормально.

Добавьте следующее после todos:

const typeDefs = `
  type Todo {
    id: String
    text: String
    completed: Boolean
  }
  type Query {
    todos: [Todo]!
  }
  type Mutation {
    createTodo(text: String!):String
    removeTodo(id: String!):String
    updateTodo(id: String!):String
  }
`;

Во-первых, мы определяем  схему  под названием Todo. Это в основном модель для Todo. Далее мы определяем наш  запрос . У нас есть только один запрос todos, который содержит массив Todo, который мы определили ранее. Знак (!) в конце означает, что он всегда будет что-то возвращать.

Вы можете думать о запросе как о способе извлечения данных с сервера GraphQL. Это похоже на запрос GET в REST. Этот запрос будет получать задачи с сервера, которые будут использоваться для заполнения клиентского приложения.

Мы также определяем 2  мутации . Мутация — это способ обновления данных на сервере. Первая мутация у нас есть  createTodo. Он принимает один текст параметра, который должен быть строкой. Затем он возвращает строку после завершения. То же самое касается  removeTodo.

Затем мы добавляем резольверы.

const resolvers = {
    Query: {
      todos: () => todos,
    },
    Mutation: {
      createTodo: (parent, args, context, info) => {
  
        return todos.push({
          id: Date.now().toString(),
          text: args.text,
          completed: false,
        });
      },
      removeTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos.splice(i, 1);
          }
        }
        return args.id;
      },
      updateTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos[i].completed = !todos[i].completed;
          }
        }
        return args.id;
      }
    }
  };

Резолверы — это функции, которые запускаются при выполнении запросов и мутаций. Здесь вы будете извлекать и обрабатывать данные, которые возвращаются клиенту. запрос todos вернул массив todos. Мутации получают 4 аргумента, но нас особенно интересуют аргументы, которые содержат данные, переданные от клиента. Мы используем эти данные, чтобы манипулировать массивом задач и возвращать результат.

 
const server = new ApolloServer({
    typeDefs,
    resolvers,
  });
  
 
  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
  });
  
  console.log(`🚀  Server ready at: ${url}`);

Наконец, мы передаем определения типов и преобразователи для создания нашего ApolloServer. Мы также обязательно используем cors() для разрешения запросов из разных источников. Затем мы обслуживаем наше приложение на порту 4000.

Из терминала запустите npm start

Если все было настроено правильно, вы должны увидеть  Now browse to http://localhost:4000/ в своем терминале. По этой ссылке мы будем отправлять все запросы и мутации из клиентского приложения.

Переход по этому URL-адресу дает вам интерактивную оболочку, в которой вы можете запускать тестовые запросы и мутации, прежде чем интегрировать их в свое клиентское приложение.

Создание нашего CRUD-приложения с помощью React

До сих пор мы могли настроить сервер и выполнять запросы из интерактивной оболочки. Давайте создадим приложение React, которое будет использовать наш API.

npx create-react-app client

Это создаст новый проект React и установит необходимые пакеты, необходимые для запуска реагирующих приложений. Далее мы установим клиент Apollo.

npm install @apollo/client graphql

Откройте  src/index.js. Удаляем там все и добавляем следующее.

import React from 'react';
import ReactDOM from "react-dom/client";
import './index.css';
import App from './App';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: "http://localhost:4000",
  cache: new InMemoryCache()

});


const root = ReactDOM.createRoot(document.getElementById('root'));        
root.render(<ApolloProvider client={client}>
  <App />
</ApolloProvider>);

Приведенный выше код настраивает приложение React, которое использует клиент Apollo GraphQL для подключения к GraphQL API по URL-адресу http://localhost:4000.

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

const client = new ApolloClient({ uri: "http://localhost:4000", cache: new InMemoryCache() });: создает экземпляр класса ApolloClient и передает параметры конфигурации.

Параметр uriуказывает URL-адрес GraphQL API, а также cacheустанавливает кэш в памяти, который можно использовать для хранения результатов запросов GraphQL.

ReactDOM.createRoot(document.getElementById('root'));: использует метод createRoot из библиотеки ReactDOM для создания корневого компонента приложения.

Вызов document.getElementById('root')находит элемент HTML с идентификатором root, который будет местом в HTML, где будет отображаться приложение React.

Компонент ApolloProviderиспользуется для упаковки Appкомпонента, и clientпараметр передается для ApolloProviderпредоставления экземпляра ApolloClient всем компонентам в приложении.

Откройте  src/App.js, очистите все в нем и добавьте следующее:

import React, { useState, input } from 'react';
import './App.css';
import { useQuery, useMutation } from '@apollo/client';
import gql from "graphql-tag";

Мы импортировали необходимые компоненты ReactJS вместе с хуками useStateand inputиз ReactJS и хуками useQueryand useMutationиз @apollo/clientбиблиотеки. Импорт gql— это graphql-tagбиблиотека, которая используется для написания запросов GraphQL в более читабельном формате.

Ниже добавьте следующее:

const READ_TODOS = gql`
  query todos{
    todos {
      id
      text
      completed
    }
  }
`;

const CREATE_TODO = gql`
  mutation CreateTodo($text: String!) {
    createTodo(text: $text)
  }
`;

const REMOVE_TODO = gql`
  mutation RemoveTodo($id: String!) {
    removeTodo(id: $id)
  }
`;

const UPDATE_TODO = gql`
  mutation UpdateTodo($id: String!) {
    updateTodo(id: $id)
  }
`;

Здесь мы определили четыре запроса GraphQL для взаимодействия с серверной частью:

  • READ_TODOS: читает список дел.
  • CREATE_TODO: создает новый элемент списка дел.
  • REMOVE_TODO: удаляет существующий элемент списка дел.
  • UPDATE_TODO: обновляет статус выполнения элемента списка дел.

Давайте отправим некоторые данные обратно на сервер, используя мутации.

function App() {
  const [todoText, setTodoText] = useState("");
  const { data, loading, error } = useQuery(READ_TODOS);
  const [createTodo] = useMutation(CREATE_TODO);
  const [deleteTodo] = useMutation(REMOVE_TODO);
  const [updateTodo] = useMutation(UPDATE_TODO);

  const handleSubmit = (e) => {
    e.preventDefault();
    createTodo({ variables: { text: todoText } });
    setTodoText("");
  };

  const handleDelete = (id) => {
    deleteTodo({ variables: { id } });
  };

  const handleUpdate = (id, completed) => {
    updateTodo({ variables: { id, completed: !completed } });
  };

  if (loading) return <p>loading...</p>;
  if (error) return <p>ERROR</p>;
  if (!data) return <p>Not found</p>;

Во фрагменте кода выше:

  • useStateловушка: код устанавливает переменную состояния todoTextс начальным значением пустой строки и функцией setTodoTextдля обновления ее значения.
  • useQueryловушка: код устанавливает ловушку запроса для чтения списка задач из серверной части с помощью запроса READ_TODOS.
  • useMutationхуки: код устанавливает 3 хука мутации для взаимодействия с серверной частью для создания, удаления и обновления элементов списка дел.

Код также определяет 3 обработчика событий для обработки взаимодействия с пользователем:

  • handleSubmit: создает новый элемент списка дел при отправке формы.
  • handleDelete: удаляет существующий элемент списка дел.
  • handleUpdate: обновляет статус выполнения элемента списка дел.

Измените форму следующим образом:

return (
    <div className="app">
      <div class="flex flex-col space-y-4 ...">

<h3 class="font-medium leading-tight text-3xl mt-0 mb-2 text-blue-600"> Welcome to Your To do app</h3>
      <form onSubmit={e => {
        e.preventDefault();
        createTodo({ variables: { text: input.value } });
        input.value = '';
        window.location.reload();
      }}>
        <div class="flex space-x-4 ...">

        <input className="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm" type="text" placeholder="Enter a task or todo" ref={node => { input = node; }}></input>
        <button className="inline-block px-6 py-2 border-2 border-blue-600 text-blue-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" type="submit">Submit</button>
      
        </div>

      </form>
      <ul>
      <div class="flex flex-col space-y-4 ">

        {data.todos.map((todo) =>
          <li key={todo.id} className="w-100">
            <div class="flex space-x-4 ...">

            <span className={todo.completed ? "done" : "pending"}>{todo.text}</span>
            <button className="inline-block px-6 py-2 border-2 border-red-600 text-red-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right" onClick={() => {
              deleteTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}> Delete</button>
             <button className={`btn inline-block px-6 py-2 border-2 border-yellow-500 text-yellow-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right ${todo.completed ? "inline-block px-6 py-2 border-2 border-green-500 text-green-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" : "btn-info"}`} onClick={() => {
              updateTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}>{todo.completed ? <span> Task Completed</span> : <span>Task Not completed</span>}</button>
         </div></li>
        )}
              </div>

      </ul>
      </div>
      </div>


  );
}

export default App;

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

И, наконец, пользовательский интерфейс оформлен с использованием классов CSS Tailwind. Вот руководство по настройке CSS Tailwind в вашем проекте, и мы также используем этот window.location.reload()метод для обновления страницы после добавления, удаления или обновления элемента списка дел.

Заключение

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

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

Полный исходный код находится на  GitHub  , так что вы можете проверить его там.

Примечание. Эта статья является обновленной версией нашей ранее опубликованной статьи — Создание приложения CRUD с помощью React и Apollo GraphQL 1.

Демонстрация нашего приложения:

Оригинальный источник статьи по адресу: https://codesource.io/

#react #apollo #graphql #tailwind 

Создайте приложение CRUD с помощью React 18.2, Apollo GraphQL 4
田辺  桃子

田辺 桃子

1679969002

如何使用 React 18.2、Apollo GraphQL 4 和 Tailwind 构建 CRUD 应用程序

在本教程中,我们将使用 React 18.2 和 Apollo GraphqL 4 构建一个 CRUD 应用程序,本教程面向想要深入研究 React graphQL 的React 开发人员。您需要 React 的实用知识才能跟进。

先决条件:

要以最有效的方式完成本教程,您需要记住一些事情。

  • 具有互联网连接的已配置计算机。
  • React、HTML 和 CSS 的基础知识。
  • 文本编辑器——您可以选择任何可以编写 JavaScript 代码的文本编辑器,例如 sublime text、nodepad++ 等。在本教程中,我们将使用 visual studio 代码。
  • NodeJS 已安装在您的计算机上。

GraphQL 快速概览

  • GraphQL 是一种用于应用程序编程接口 (API) 的开源数据查询和操作语言。
  • 与其他 API 格式不同,GraphQL 允许 API 使用者从应用程序的服务器请求特定数据,而不会收到不必要的信息。
  • 相比之下,传统的 REST API 架构提供固定的数据结构,然后依靠客户端过滤掉任何不需要的信息。
  • GraphQL 的设计在安全性方面提供了优势。因为 GraphQL 不会返回客户端未明确请求的数据,所以它的使用降低了信息泄露问题的风险。
  • GraphQL 与语言无关,可与多种编程语言一起使用,包括 JavaScript、Java、Python 和 Ruby。
  • GraphQL 实现了一个模式来定义可以查询的数据类型以及这些类型之间的关系。

让我们看一个例子。假设您的应用需要从服务器获取用户的详细信息。使用 REST,您的响应可能如下所示:

{
  user_details: {
    id: 2348,
    name: “Deven Rathore”,
    email: “deven@example.com”
  }
}

使用 GraphQL,您可以准确地请求您想要的数据,并确保您只会获得您请求的数据。假设您需要的只是用户 ,您的响应将如下所示:

{
  user_details: {
    name: “Deven Rathore”
  }
}

使用 GraphQL,您只需要一个端点,您可以将 查询 和 变更发送到该端点 来操作您的数据。

阿波罗的快速概述:

Apollo 是一组工具和库(由 Meteor Development Group 开发),用于帮助开发人员在他们的项目中使用 GraphQL。Apollo 有一个客户端和一个服务器。 

阿波罗服务器:

Apollo 服务器是一个库,旨在简化 GraphQL 方案与 Node.js 及其支持的框架中的 HTTP(超文本传输​​协议)服务器的连接。 

阿波罗客户:

Apollo 客户端是一个库,旨在简化使用 JavaScript 从 GraphQL 服务器获取数据的过程。

它是一个功能齐全的缓存 GraphQL 客户端。它可以与任何现代 JavaScript 框架一起使用,例如 Vue.js、React、Angular 等。

开始之前:

通过Pluralsight 提供的综合 React 课程,将您的 Web 开发技能提升到一个新的水平。这个行业领先的学习平台提供专家指导的课程,涵盖 React 中的最新技术和最佳实践。现在就开始学习,看着你的信心和能力随着每一节课的增长而增长。

对于本教程,我们将运行两台服务器:一台服务于我们的后端,另一台服务于我们的 React CRUD 应用程序。我们先从后端开始。

设置阿波罗服务器

从您的终端创建一个新目录并安装所需的包:

mkdir todo-graphql && cd todo-graphql

在此目录中,创建另一个文件夹:

mkdir server

服务器将包含我们的 GraphQL 服务器。切换到终端中的服务器文件夹:

cd server

现在,安装以下内容:

npm install @apollo/server graphql

在根目录下创建一个名为的文件 index.js 并添加以下内容:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

let todos = [
    {
      id: Date.now().toString(),
      text: 'Hello from GraphQL',
      completed: true,
    },
  ];

我们将要创建的应用程序是一个待办事项列表应用程序,它将跟踪待办事项以及它们是否已完成。

我们会将待办事项存储在一个数组中,但该数组可以代替数据库或文件。为了简单起见,我们将使用数组。

每个待办事项都有一个 id、text 和 completed。到目前为止,一切都很好。

在 todos 之后添加以下内容:

const typeDefs = `
  type Todo {
    id: String
    text: String
    completed: Boolean
  }
  type Query {
    todos: [Todo]!
  }
  type Mutation {
    createTodo(text: String!):String
    removeTodo(id: String!):String
    updateTodo(id: String!):String
  }
`;

首先,我们定义一个 名为 Todo 的模式 。这基本上是 Todo 的模型。接下来,我们定义我们的 查询。我们只有一个查询 todos,其中包含我们之前定义的一组 Todo。末尾的 (!) 表示它将始终返回一些内容。

您可以将查询视为从 GraphQL 服务器获取数据的方式。这就像 REST 中的 GET 请求。此查询将从用于填充客户端应用程序的服务器获取待办事项。

我们还定义了 2 个 突变。突变是一种更新服务器上数据的方法。我们的第一个突变是 createTodo. 它接受一个参数文本,它必须是一个字符串。然后它在完成后返回一个字符串。同样的事情也适用于 removeTodo

接下来,我们添加解析器。

const resolvers = {
    Query: {
      todos: () => todos,
    },
    Mutation: {
      createTodo: (parent, args, context, info) => {
  
        return todos.push({
          id: Date.now().toString(),
          text: args.text,
          completed: false,
        });
      },
      removeTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos.splice(i, 1);
          }
        }
        return args.id;
      },
      updateTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos[i].completed = !todos[i].completed;
          }
        }
        return args.id;
      }
    }
  };

解析器是在进行查询和更改时运行的函数。这是您将获取和操作返回给客户端的数据的地方。todos 查询返回了待办事项数组。突变接收 4 个参数,但我们对 args 特别感兴趣,它包含从客户端传递的数据。我们使用该数据来操作待办事项数组并返回结果。

 
const server = new ApolloServer({
    typeDefs,
    resolvers,
  });
  
 
  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
  });
  
  console.log(`🚀  Server ready at: ${url}`);

最后,我们传递类型定义和解析器来创建我们的 ApolloServer。我们还确保使用 cors() 来允许跨域请求。然后我们在端口 4000 上提供我们的应用程序。

从终端运行 npm start

如果一切设置正确,您应该会 Now browse to http://localhost:4000/ 在终端中看到。该链接是我们从客户端应用程序发送所有查询和变更的地方。

导航到该 URL 会为您提供一个交互式 shell,您可以在其中运行测试查询和变更,然后再将它们集成到您的客户端应用程序中。

使用 React 构建我们的 CRUD 应用程序

到目前为止,我们已经能够设置服务器并从交互式 shell 运行查询。让我们创建将使用我们的 API 的 React 应用程序。

npx create-react-app client

这将创建一个新的 React 项目并安装运行 React 应用程序所需的必要包。接下来,我们将安装 Apollo Client。

npm install @apollo/client graphql

打开 src/index.js。删除那里的所有内容并添加以下内容。

import React from 'react';
import ReactDOM from "react-dom/client";
import './index.css';
import App from './App';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: "http://localhost:4000",
  cache: new InMemoryCache()

});


const root = ReactDOM.createRoot(document.getElementById('root'));        
root.render(<ApolloProvider client={client}>
  <App />
</ApolloProvider>);

上面的代码设置了一个 React 应用程序,它使用 Apollo GraphQL 客户端连接到位于 URL 的 GraphQL API http://localhost:4000

ApolloProvider 组件将用于包装 React 应用程序并向所有需要进行 GraphQL 查询或更改的组件提供 ApolloClient 实例。ApolloClient 实例在下一步中创建。

const client = new ApolloClient({ uri: "http://localhost:4000", cache: new InMemoryCache() });:创建 ApolloClient 类的实例并传入配置选项。

uri选项指定 GraphQL API 的 URL,该cache选项设置一个内存缓存,可用于存储 GraphQL 查询的结果。

ReactDOM.createRoot(document.getElementById('root'));:使用 ReactDOM 库中的 createRoot 方法为应用程序创建根组件。

document.getElementById('root')调用找到 ID 为 的 HTML 元素root,这将是 HTML 中将呈现 React 应用程序的位置。

ApolloProvider组件用于包装App组件,并将client选项传递给 以ApolloProvider向应用程序中的所有组件提供 ApolloClient 实例。

打开 src/App.js,清除其中的所有内容并添加以下内容:

import React, { useState, input } from 'react';
import './App.css';
import { useQuery, useMutation } from '@apollo/client';
import gql from "graphql-tag";

我们导入了必要的 ReactJS 组件,以及来自 ReactJS 的useStateinput钩子,以及来自库的useQuery和钩子。导入是库,用于以更具可读性的格式编写 GraphQL 查询。useMutation@apollo/clientgqlgraphql-tag

在此之下,添加以下内容:

const READ_TODOS = gql`
  query todos{
    todos {
      id
      text
      completed
    }
  }
`;

const CREATE_TODO = gql`
  mutation CreateTodo($text: String!) {
    createTodo(text: $text)
  }
`;

const REMOVE_TODO = gql`
  mutation RemoveTodo($id: String!) {
    removeTodo(id: $id)
  }
`;

const UPDATE_TODO = gql`
  mutation UpdateTodo($id: String!) {
    updateTodo(id: $id)
  }
`;

在这里,我们定义了四个 GraphQL 查询来与后端交互:

  • READ_TODOS:阅读待办事项列表。
  • CREATE_TODO:创建一个新的待办事项。
  • REMOVE_TODO:删除现有的待办事项。
  • UPDATE_TODO:更新待办事项的完成状态。

让我们使用突变将一些数据发送回服务器。

function App() {
  const [todoText, setTodoText] = useState("");
  const { data, loading, error } = useQuery(READ_TODOS);
  const [createTodo] = useMutation(CREATE_TODO);
  const [deleteTodo] = useMutation(REMOVE_TODO);
  const [updateTodo] = useMutation(UPDATE_TODO);

  const handleSubmit = (e) => {
    e.preventDefault();
    createTodo({ variables: { text: todoText } });
    setTodoText("");
  };

  const handleDelete = (id) => {
    deleteTodo({ variables: { id } });
  };

  const handleUpdate = (id, completed) => {
    updateTodo({ variables: { id, completed: !completed } });
  };

  if (loading) return <p>loading...</p>;
  if (error) return <p>ERROR</p>;
  if (!data) return <p>Not found</p>;

在上面的代码片段中:

  • useStatehook:代码设置了一个todoText初始值为空字符串的状态变量,以及一个setTodoText更新其值的函数。
  • useQuery钩子:代码设置一个查询钩子以使用查询从后端读取待办事项列表READ_TODOS
  • useMutationhooks:代码设置了3个mutation hooks来与后端交互来创建、删除和更新待办事项。

该代码还定义了 3 个事件处理程序来处理用户交互:

  • handleSubmit:在提交表单时创建一个新的待办事项。
  • handleDelete:删除现有的待办事项。
  • handleUpdate:更新待办事项的完成状态。

像这样修改表格:

return (
    <div className="app">
      <div class="flex flex-col space-y-4 ...">

<h3 class="font-medium leading-tight text-3xl mt-0 mb-2 text-blue-600"> Welcome to Your To do app</h3>
      <form onSubmit={e => {
        e.preventDefault();
        createTodo({ variables: { text: input.value } });
        input.value = '';
        window.location.reload();
      }}>
        <div class="flex space-x-4 ...">

        <input className="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm" type="text" placeholder="Enter a task or todo" ref={node => { input = node; }}></input>
        <button className="inline-block px-6 py-2 border-2 border-blue-600 text-blue-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" type="submit">Submit</button>
      
        </div>

      </form>
      <ul>
      <div class="flex flex-col space-y-4 ">

        {data.todos.map((todo) =>
          <li key={todo.id} className="w-100">
            <div class="flex space-x-4 ...">

            <span className={todo.completed ? "done" : "pending"}>{todo.text}</span>
            <button className="inline-block px-6 py-2 border-2 border-red-600 text-red-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right" onClick={() => {
              deleteTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}> Delete</button>
             <button className={`btn inline-block px-6 py-2 border-2 border-yellow-500 text-yellow-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right ${todo.completed ? "inline-block px-6 py-2 border-2 border-green-500 text-green-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" : "btn-info"}`} onClick={() => {
              updateTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}>{todo.completed ? <span> Task Completed</span> : <span>Task Not completed</span>}</button>
         </div></li>
        )}
              </div>

      </ul>
      </div>
      </div>


  );
}

export default App;

呈现代码检查查询结果的加载、错误和数据状态,并显示适当的消息。UI 显示用于添加新待办事项的表单、现​​有待办事项列表以及用于删除和更新待办事项的按钮。

最后,使用 Tailwind CSS 类对 UI 进行样式设置,这是在您的项目中设置 Tailwind CSS的指南,我们还使用该window.location.reload()方法在添加、删除或更新待办事项后刷新页面。

结论

GraphQL 非常令人兴奋,因为它解决了 REST 的一些问题。使用 Apollo GraphQL 使我们能够灵活地轻松使用 GraphQL,而无需过多的开销成本。通过结合这些技术的强大功能,可以创建动态且用户友好的待办事项应用程序,以更高效和有效的方式处理各种任务和管理数据。

GraphQL 和 React 的集成提供了无缝的数据获取体验,而 TailwindCSS 为应用程序的用户界面提供了简洁且可自定义的设计。借助这些工具,开发人员可以获得满足任何现代 Web 应用程序要求的高质量结果。

完整的代码源在 GitHub上 ,所以你可以在那里查看。

注意:本文是我们之前发表的文章——使用 React 和 Apollo GraphQL 1构建 CRUD 应用程序的更新版本

我们的应用程序演示:

文章原文出处:https: //codesource.io/

#react #apollo #graphql #tailwind 

如何使用 React 18.2、Apollo GraphQL 4 和 Tailwind 构建 CRUD 应用程序
Desmond  Gerber

Desmond Gerber

1679968641

How to Build A CRUD App with React 18.2, Apollo GraphQL 4 & Tailwind

In this tutorial, we’ll build a CRUD app with React 18.2 and Apollo GraphqL 4, This tutorial is targeted at React developers who want to dive into React graphQL. You need workable knowledge of React to follow along.

Prerequisite:

To complete this tutorial in the most effective way you need to keep some things in your mind.

  • A configured computer with an internet connection.
  • Basic knowledge of React, HTML, and CSS.
  • A text editor – You can choose any text editor on which you may write JavaScript code such as sublime text, nodepad++, etc. In this tutorial, we will use visual studio code.
  • NodeJS is installed on your computer.

A Quick Overview of GraphQL

  • GraphQL is an open-source data query and manipulation language for application programming interfaces (APIs).
  • GraphQL, unlike other API formats, allows an API consumer to request specific data from an application’s server without receiving unnecessary information.
  • In contrast, traditional REST API architectures provide a fixed data structure and then rely on clients to filter out any unnecessary information.
  • GraphQL’s design provides an advantage in terms of security. Because GraphQL does not return data that the client has not explicitly requested, its use reduces the risk of information disclosure issues.
  • GraphQL is language-independent and works with a wide range of programming languages, including JavaScript, Java, Python, and Ruby.
  • GraphQL implements a schema to define the types of data that can be queried in addition to the relationships between those types.

Let’s look at an example. Let’s say your app needs to fetch a user’s details from the server. Using REST, your response may look like this:

{
  user_details: {
    id: 2348,
    name: “Deven Rathore”,
    email: “deven@example.com”
  }
}

With GraphQL, you can request exactly the data you want and be sure you’ll get only what you asked for. Assuming what you need is just the user’s name, your response would look like this:

{
  user_details: {
    name: “Deven Rathore”
  }
}

With GraphQL, you get just one endpoint to which you send queries and mutations to manipulate your data.

A Quick Overview of Apollo:

Apollo is a set of tools and libraries developed (by Meteor Development Group) to help developers use GraphQL in their projects. Apollo has a client and a server. 

Apollo Server:

Apollo server is a library built to ease the connection of a GraphQL scheme to an HTTP (Hypertext Transfer Protocol) server in Node.js and its supported frameworks. 

Apollo Client:

Apollo client is a library built to ease the fetching of data from GraphQL servers with JavaScript.

It is a fully-featured caching GraphQL client. It can be used with any modern JavaScript framework like Vue.js, React, Angular, and more.

Before Getting started:

Take your web development skills to the next level with the comprehensive React course offered by Pluralsight. This industry-leading learning platform offers expert-led lessons that cover the latest techniques and best practices in React. Start learning now and watch your confidence and abilities grow with each lesson.

For this tutorial, we’ll have two servers running: one serving our backend and the second serving our React CRUD application. We’ll start with the backend first.

Setup Apollo Server

From your terminal, create a new directory and install the required packages:

mkdir todo-graphql && cd todo-graphql

Inside this directory, create another folder:

mkdir server

the server will contain our GraphQL server. Switch to the server folder in your terminal:

cd server

Now, install the following:

npm install @apollo/server graphql

Create a file in the root directory called index.js and add the following:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

let todos = [
    {
      id: Date.now().toString(),
      text: 'Hello from GraphQL',
      completed: true,
    },
  ];

The application we’ll be creating is a to-do list application that would keep track of todos and if they have been completed or not.

We’ll store todos in an array but the array can be substituted for a database or file. To keep things simple, we’ll be using an array.

Each todo has an id, text, and completed. So far so good.

Add the following after todos:

const typeDefs = `
  type Todo {
    id: String
    text: String
    completed: Boolean
  }
  type Query {
    todos: [Todo]!
  }
  type Mutation {
    createTodo(text: String!):String
    removeTodo(id: String!):String
    updateTodo(id: String!):String
  }
`;

First, we define a schema called Todo. This is basically a model for a Todo. Next, we define our query. We have just one query todos which contains an array of Todo we defined earlier. The (!) at the end signifies that it will always return something.

You can think of a query as the way you fetch data from a GraphQL server. It’s like a GET request in REST. This query will fetch todos from the server that will be used to populate the client app.

We also define 2 mutations. A mutation is a way to update data on the server. The first mutation we have is createTodo. It accepts one parameter text, which must be a String. It then returns a string after it’s finished. The same thing goes for removeTodo.

Next, we add the resolvers.

const resolvers = {
    Query: {
      todos: () => todos,
    },
    Mutation: {
      createTodo: (parent, args, context, info) => {
  
        return todos.push({
          id: Date.now().toString(),
          text: args.text,
          completed: false,
        });
      },
      removeTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos.splice(i, 1);
          }
        }
        return args.id;
      },
      updateTodo: (parent, args, context, info) => {
        for (let i in todos) {
          if (todos[i].id === args.id) {
            todos[i].completed = !todos[i].completed;
          }
        }
        return args.id;
      }
    }
  };

Resolvers are the functions that run when queries and mutations are made. This is where you would fetch and manipulate the data that’s returned to the client. todos query returned the array of todos. Mutations receive 4 arguments but we are particularly interested in args, which contains the data passed from the client. We use that data to manipulate the array of todos and return a result.

 
const server = new ApolloServer({
    typeDefs,
    resolvers,
  });
  
 
  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
  });
  
  console.log(`🚀  Server ready at: ${url}`);

Lastly, we pass the type definitions and resolvers to create our ApolloServer. We also make sure to use cors() to allow cross-origin requests. Then we serve our app on port 4000.

From the terminal, run npm start

If everything was set up correctly, you should see Now browse to http://localhost:4000/ in your terminal. That link is where we would be sending all queries and mutations from the client app.

Navigating to that URL gives you an interactive shell where you can run test your queries and mutations before integrating them into your client app.

Building our CRUD app with React

So far we’ve been able to set up the server and run queries from the interactive shell. Let’s create the React app that will make use of our API.

npx create-react-app client

This will create a new React project and install the necessary packages required to run react applications. Next, we’ll install Apollo Client.

npm install @apollo/client graphql

Open up src/index.js. Remove everything there and add the following.

import React from 'react';
import ReactDOM from "react-dom/client";
import './index.css';
import App from './App';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: "http://localhost:4000",
  cache: new InMemoryCache()

});


const root = ReactDOM.createRoot(document.getElementById('root'));        
root.render(<ApolloProvider client={client}>
  <App />
</ApolloProvider>);

The above code sets up a React application that uses the Apollo GraphQL client to connect to a GraphQL API at the URL http://localhost:4000.

The ApolloProvider component will be used to wrap the React application and provide the ApolloClient instance to all the components that need to make GraphQL queries or mutations. The ApolloClient instance is created in the next step.

const client = new ApolloClient({ uri: "http://localhost:4000", cache: new InMemoryCache() });: creates an instance of the ApolloClient class and passes in the configuration options.

The uri option specifies the URL of the GraphQL API, and the cache option sets up an in-memory cache that can be used to store the results of GraphQL queries.

ReactDOM.createRoot(document.getElementById('root'));: uses the createRoot method from the ReactDOM library to create a root component for the application.

The document.getElementById('root') call finds the HTML element with the ID of root, which will be the location in the HTML where the React application will be rendered.

The ApolloProvider component is used to wrap the App component, and the client option is passed to the ApolloProvider to provide the ApolloClient instance to all the components in the application.

Open up src/App.js, clear everything in it and add the following:

import React, { useState, input } from 'react';
import './App.css';
import { useQuery, useMutation } from '@apollo/client';
import gql from "graphql-tag";

We imported the necessary ReactJS components, along with the useState and input hooks from ReactJS, and the useQuery and useMutation hooks from the @apollo/client library. The gql import is the graphql-tag library, which is used to write GraphQL queries in a more readable format.

Below that, add the following:

const READ_TODOS = gql`
  query todos{
    todos {
      id
      text
      completed
    }
  }
`;

const CREATE_TODO = gql`
  mutation CreateTodo($text: String!) {
    createTodo(text: $text)
  }
`;

const REMOVE_TODO = gql`
  mutation RemoveTodo($id: String!) {
    removeTodo(id: $id)
  }
`;

const UPDATE_TODO = gql`
  mutation UpdateTodo($id: String!) {
    updateTodo(id: $id)
  }
`;

Here, we defined four GraphQL queries to interact with the back end:

  • READ_TODOS: reads the list of to-dos.
  • CREATE_TODO: creates a new to-do item.
  • REMOVE_TODO: removes an existing to-do item.
  • UPDATE_TODO: updates the completion status of a to-do item.

Let’s send some data back to the server using mutations.

function App() {
  const [todoText, setTodoText] = useState("");
  const { data, loading, error } = useQuery(READ_TODOS);
  const [createTodo] = useMutation(CREATE_TODO);
  const [deleteTodo] = useMutation(REMOVE_TODO);
  const [updateTodo] = useMutation(UPDATE_TODO);

  const handleSubmit = (e) => {
    e.preventDefault();
    createTodo({ variables: { text: todoText } });
    setTodoText("");
  };

  const handleDelete = (id) => {
    deleteTodo({ variables: { id } });
  };

  const handleUpdate = (id, completed) => {
    updateTodo({ variables: { id, completed: !completed } });
  };

  if (loading) return <p>loading...</p>;
  if (error) return <p>ERROR</p>;
  if (!data) return <p>Not found</p>;

In the code snippet above:

  • useState hook: The code sets up a state variable, todoText, with an initial value of an empty string, and a function setTodoText to update its value.
  • useQuery hook: The code sets up a query hook to read the list of to-dos from the back-end using the READ_TODOS query.
  • useMutation hooks: The code sets up 3 mutation hooks to interact with the back-end to create, delete and update to-do items.

The code also defines 3 event handlers to handle user interactions:

  • handleSubmit: creates a new to-do item when the form is submitted.
  • handleDelete: deletes an existing to-do item.
  • handleUpdate: updates the completion status of a to-do item.

Modify the form like so:

return (
    <div className="app">
      <div class="flex flex-col space-y-4 ...">

<h3 class="font-medium leading-tight text-3xl mt-0 mb-2 text-blue-600"> Welcome to Your To do app</h3>
      <form onSubmit={e => {
        e.preventDefault();
        createTodo({ variables: { text: input.value } });
        input.value = '';
        window.location.reload();
      }}>
        <div class="flex space-x-4 ...">

        <input className="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm" type="text" placeholder="Enter a task or todo" ref={node => { input = node; }}></input>
        <button className="inline-block px-6 py-2 border-2 border-blue-600 text-blue-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" type="submit">Submit</button>
      
        </div>

      </form>
      <ul>
      <div class="flex flex-col space-y-4 ">

        {data.todos.map((todo) =>
          <li key={todo.id} className="w-100">
            <div class="flex space-x-4 ...">

            <span className={todo.completed ? "done" : "pending"}>{todo.text}</span>
            <button className="inline-block px-6 py-2 border-2 border-red-600 text-red-600 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right" onClick={() => {
              deleteTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}> Delete</button>
             <button className={`btn inline-block px-6 py-2 border-2 border-yellow-500 text-yellow-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out float-right ${todo.completed ? "inline-block px-6 py-2 border-2 border-green-500 text-green-500 font-medium text-xs leading-tight uppercase rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out" : "btn-info"}`} onClick={() => {
              updateTodo({ variables: { id: todo.id } });
              window.location.reload();
            }}>{todo.completed ? <span> Task Completed</span> : <span>Task Not completed</span>}</button>
         </div></li>
        )}
              </div>

      </ul>
      </div>
      </div>


  );
}

export default App;

The Render code checks for loading, error, and data states of the query result, and displays the appropriate messages. The UI displays a form to add a new to-do item, a list of existing to-do items, and buttons to delete and update the to-do items.

And finally, The UI is styled using Tailwind CSS classes, Here is a guide to setting up tailwind CSS in your project and we are also using the window.location.reload() method to refresh the page after adding, deleting, or updating a to-do item.

Conclusion

GraphQL is quite exciting as it tackles some of the issues with REST. Using Apollo GraphQL gives us the flexibility to use GraphQL with ease without too much overhead cost. By combining the powerful features of these technologies, it is possible to create a dynamic and user-friendly To-do app that can handle various tasks and manage data in a more efficient and effective way.

The integration of GraphQL and React provides a seamless data-fetching experience, while TailwindCSS provides a clean and customizable design for the app’s user interface. With these tools, developers can achieve high-quality results that meet the requirements of any modern web application.

The full Code source is on GitHub so you can check it out there.

Note: This article is an updated version of our previously published article – Build a CRUD Application with React and Apollo GraphQL1

Demo of our app:

Original article source at: https://codesource.io/

#react #apollo #graphql #tailwind 

How to Build A CRUD App with React 18.2, Apollo GraphQL 4 & Tailwind

Руководство для начинающих с Apollo Vue GraphQL

Введение

Vue Js — это современная среда JavaScript, популярная для создания SPA. Чтобы сделать работу разработчиков Vue удобной, клиент Vue Apollo и библиотека управления состоянием функционируют вместе с серверным языком запросов GraphQL для формирования сложных пользовательских интерфейсов.

В этом блоге мы использовали платформу Apollo Vue вместе с сервером Vue GraphQL для безупречной связи. Прежде чем приступить к руководству по Apollo Vue GraphQL , давайте перейдем к основам.

Что такое GraphQL?

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

Некоторые из популярных фреймворков, используемых с GraphQL, — это Apollo, Hasura, GraphQL Yoga и другие. Преимущества использования GraphQL заключаются в том, что он делает API-интерфейсы быстрыми и гибкими, а разработчики находят его простым в использовании.

Что такое Аполлон?

Apollo — наиболее предпочтительная среда GraphQL, поскольку она предоставляет как интерфейсные, так и серверные решения. Он предоставляет большое количество инструментов для преобразования серверной части в GraphQL API и, следовательно, легкого взаимодействия с интерфейсом.

Зачем нужен Vue GraphQL?

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

Потребность в Vue GraphQL

Процесс установки Apollo Vue GraphQL

Давайте начнем с процесса подключения GraphQL API к внешнему интерфейсу Vue Js с использованием клиентской среды Apollo.

Шаг 1: Vue Apollo с созданием сервера GraphQL

Вначале создайте одну папку проекта и внутри этой папки используйте файл package.json ниже для создания сервера GraphQL.

Пакет.json

"name": '"graphgl-server",
"version": "1.0.0",
"description": "",

"author": "",
> Debug

"scripts": {
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"main": "index.js",
"dependencies": {
    "cors": "~2.8.5",
    "express": "~4.18.2",
    "express—-graphql": "70.12.00",
   "nodemon": "2.0.20"
},
"keywords": [],
"license": "ISC"
}

Шаг 2. Создайте файл App.js

После этого давайте создадим файл app.js в той же корневой папке. Этот файл app.js предназначен для создания схемы для баз данных. Итак, вы можете создать базовую схему, как показано ниже.

App.js

const express = require("express");
const cors = require("cors");
const {
    graphqlHTTP
} = require("express-graphql");
const {
    GraphQLObjectType,
    GraphQLInt,
    GraphQLString,
    GraphQLList,
    GraphQLSchema,
    GraphQLNonNull,
} = require("graphql");
const app = express();

app.use(cors());

let userData = [{
        id: 1,
        firstName: "John",
        lastName: "Doe",
        age: 22
    },
    {
        id: 2,
        firstName: "Mark",
        lastName: "Smith",
        age: 30
    },
    {
        id: 3,
        firstName: "Robert",
        lastName: "Hunt",
        age: 35
    },
];

const userType = new GraphQLObjectType({
    name: "User",
    description: "UserDetails",
    fields: {
        id: {
            type: GraphQLNonNull(GraphQLInt),
        },
        firstName: {
            type: GraphQLNonNull(GraphQLString),
        },
        lastName: {
            type: GraphQLNonNull(GraphQLString),
        },
        age: {
            type: GraphQLNonNull(GraphQLInt),
        },
    },
});

const rootQuery = new GraphQLObjectType({
    name: "RootQuery",
    description: "This is root query",
    fields: {
        users: {
            type: GraphQLList(userType),
            resolve: () => userData,
        },

        user: {
            type: userType,
            args: {
                id: {
                    type: GraphQLInt
                },
            },
            resolve: (_, args) => userData.find((data) => data.id === args.id),
        },
    },
});

const rootMutation = new GraphQLObjectType({
    name: "RootMutation",
    description: "This is root mutation",
    fields: {
        addUser: {
            type: userType,
            args: {
                firstName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                lastName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                age: {
                    type: GraphQLNonNull(GraphQLInt)
                },
            },
            resolve: (_, args) => {
                const newUser = {
                    id: userData.length + 1,
                    firstName: args.firstName,
                    lastName: args.lastName,
                    age: args.age,
                };
                userData.push(newUser);
                return newUser;
            },
        }
    },
});

const schema = new GraphQLSchema({
    query: rootQuery,
    mutation: rootMutation
});

app.use(
    "/graphql",
    graphqlHTTP({
        schema,
        graphiql: true,
    })
);

const PORT = 3001;

app.listen(PORT, () => {
    console.log(`Listening on port - ${PORT}`);
});

После создания файла app.js нам нужно выполнить npm install , чтобы установить все необходимые зависимости. После успешной установки всех зависимостей нам нужно запустить команду npm run dev для запуска сервера GraphQL. Как только все это будет сделано, давайте создадим приложение Vue в той же корневой папке.

Шаг 3: Создайте приложение Vue

Здесь мы использовали Vue 3 для создания нашего приложения и убедились, что вам нужно использовать версию узла ниже v17.

Vue 3 для создания нашего приложения

 Создать Vue-приложение

Теперь перейдите к нашему приложению и запустите его.

Перейти к нашему приложению

После успешного запуска локального сервера он будет выглядеть следующим образом:

Домашняя страница приложения Vuejs

Шаг 4: Полная конфигурация клиента Apollo

Установите эти пакеты для интеграции Apollo Vue GraphQL .

npm install --save @vue/apollo-option

npm install --save @apollo/client

npm install --save graphql

npm install --save graphql-tag

интегрировать Apollo Vue GraphQLТакже установите реакцию, чтобы избежать ошибок, говорящих о необходимости установки «реакции».

npm install react

установить реагировать, чтобы избежать ошибокФайл Vue3 package.json после установки вышеуказанной зависимости.

Файл пакета Vue3.json

Шаг 5: Создайте провайдера Apollo

После настройки клиента перейдите в приложение vue и создайте файл apollo.provider.js в папке src.

apollo.provider.js:

  • Здесь «InMemoryCache» загружается из «@apollo/client». Причина использования «InMemoryCache» заключается в том, чтобы сохранить ответ API в кеше, чтобы последующий запрос загружал данные из кеша вместо того, чтобы снова и снова вызывать API.
  • 'ApolloClient' загружается из '@apollo/client'. Здесь мы должны передать такие конфигурации, как «кеш» и нашу конечную точку Graphql.
  • Также определен 'createApolloProvider', который загружается из '@vue/apollo-option'.
import { InMemoryCache, ApolloClient } from "@apollo/client";
import { createApolloProvider } from "@vue/apollo-option";

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
  cache,
  uri: "http://localhost:3001/graphql",
});

export const provider = createApolloProvider({
defaultClient: apolloClient,
});

После добавления приведенного выше кода в файл apollo.provider.js нам нужно импортировать apolloProvider из файла «apollo-.provider.js» и интегрировать его в экземпляр vue.

аполлонпровайдер

Поскольку мы ознакомились с учебным пособием по Apollo Vue GraphQL, давайте рассмотрим некоторые ключевые моменты, которые необходимо знать о GraphQL.

Запрос:

Запрос используется для получения данных из конечной точки GraphQL.

Ниже 'graphql.js' запрос getAllUsers извлекает данные всех пользователей из конечной точки GraphQL.

Запрос с параметрами:

Если мы хотим передать динамическое значение нашему запросу, мы используем понятие «переменная». Используя переменные, мы можем передать параметры запроса. В приведенном ниже примере запроса «getSingleUser» «$id» — это тип переменной, значение которой динамически заменяется объектом переменной.

Мутация:

Мутация — это операция GraphQL, которая позволяет вам вставлять новые данные или изменять существующие данные на стороне сервера. Вы можете думать о мутациях GraphQL как об эквиваленте запросов POST, PUT, PATCH и DELETE в REST. В приведенном ниже файле мы используем мутацию для добавления пользователя.

Шаг 6: Добавление запросов через GraphQL

Теперь давайте создадим файл graphql.js внутри папки src, чтобы добавить запросы. Для этого нам нужно импортировать gql из 'graphql-tag'.

import agql from "graphgl-tag"; 

export const getAllUsersQuery = gql
 query users {
  users {
   id
   firstName
   lastName
   age
  }
 }
;

export const getSingleUserQuery = gql°
  query user($id: Int!) {
   user (id: $id) {
    id
    firstName
    lastName
    age
   }
  }
 ;

export const addUserMutation = gql
   mutation addUser($firstName: String!, $lastName: String!, $age: Int!) {
    addUser(firstName: $firstName, lastName: $lastName, age: $age) {
    id
    firstName
    lastName
    age
   }
  }
 ;

После этого давайте посмотрим, как мы можем использовать запрос GraphQL в нашем компоненте. Для этого предположим, что у нас есть запрос getAllUser , как мы определили выше в файле graphql.js.

AllUsers.vue

<template>
  <div>
    <div v-if="users">
     <div v-for="user in users'">
      {{ user.firstName }}
   </div>
  </div>
   <div v-else>No user found</div>
  </div>
</template>

Для получения SingleUser по id нам нужно указать некоторые параметры внутри запроса. Итак, для этого предположим один компонент для SingleUser.vue.

SingleUser.vue

<template>
  <div>
   <div>
    <h5>{{ user.firstName + " " + user.lastName }}</h5>
     <p>Age — {{ user.age }}</p>
    </div>
  </div>
</template>

Теперь давайте предположим, что нам нужно добавить пользователя, поэтому для этого нам нужно импортировать addUserMutation из файла graphql.js, который мы уже определили в файле graphql.js выше.

AddUser.view

<template>
<div>
<form class="w-1/2 ml-auto mr-auto" @submit.prevent="addUser">
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model="fname" name="floating_company" id="floating_company" placeholde
</div>
</div>
<div>
<div class="relative z-@ w-full mb-6 group"
<input type="text" v-model="lname" name="floating_company" id="floating_company" placeholder="LastName" required />
</div>
</div>
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model.number="age" name="floating_company" id="floating_company" placeholder="Age" required />
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</template>

В целом, вы можете создать любой проект Vue, используя GraphQL с помощью Vue Apollo.

Заключение

Мы надеемся, что приведенный выше пример учебника по Apollo Vue GraphQL был полезен для решения ваших вопросов и дал вам общее представление об использовании клиента Apollo с Vue 3 GraphQL. Чтобы получить доступ к большему количеству таких проницательных руководств по Vue Js, ознакомьтесь с нашими руководствами по Vue .

Оригинальный источник статьи:   https://www.bacancytechnology.com/

#apollo #vue #graphql 

Руководство для начинающих с Apollo Vue GraphQL
津田  淳

津田 淳

1678342800

Apollo Vue GraphQL 初学者指南

介绍

Vue Js 是一个现代的 javascript 框架,在构建 SPA 方面很流行。为了方便 Vue 开发者体验,Vue Apollo 客户端和状态管理库功能与服务器查询语言 GraphQL 一起构成了复杂的 UI。

在这篇博客中,我们使用了 Apollo Vue 框架和 Vue GraphQL 服务器来执行完美的通信。在开始Apollo Vue GraphQL教程之前,让我们了解一下基础知识。

什么是 GraphQL?

2015 年,Facebook 发布了GraphQL,这是一种用于 Web API 的查询语言,可以简化前端和后端之间的通信。它由用于服务器的模式语言和用于客户端的查询语言组成。作为一个开放规范,任何人都可以将它与他们的版本框架一起使用。它可以与任何选择的后端语言一起使用来创建有效的 GraphQL 服务器。

与 GraphQL 一起使用的一些流行框架有 Apollo、Hasura、GraphQL Yoga 等。使用 GraphQL 的好处是它使 API 快速、灵活,并且开发人员发现它易于使用。

什么是阿波罗?

Apollo 是最受欢迎的 GraphQL 框架,因为它同时提供前端和后端解决方案。它提供了大量工具来将后端转换为 GraphQL API,从而轻松地与前端交互。

为什么需要 Vue GraphQL?

GraphQL 服务于服务端,当前端想要查询或获取数据时,GraphQL 客户端库提供了解决方案,Vue GraphQL 就是一个这样的解决方案。每个前端框架都有几个这样的库。

需要 Vue GraphQL

Apollo Vue GraphQL 安装过程

让我们从使用 Apollo 客户端框架将 GraphQL API 连接到 Vue Js 前端的过程开始。

第 1 步:使用 GraphQL 服务器创建 Vue Apollo

一开始创建一个项目文件夹,并在该文件夹内使用下面的 package.json 文件创建 GraphQL 服务器。

包.json

"name": '"graphgl-server",
"version": "1.0.0",
"description": "",

"author": "",
> Debug

"scripts": {
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"main": "index.js",
"dependencies": {
    "cors": "~2.8.5",
    "express": "~4.18.2",
    "express—-graphql": "70.12.00",
   "nodemon": "2.0.20"
},
"keywords": [],
"license": "ISC"
}

第 2 步:创建 App.js 文件

之后让我们在同一个根文件夹中创建一个 app.js 文件。该 app.js 文件用于为数据库创建模式。因此,您可以按照以下示例创建基本架构。

应用程序.js

const express = require("express");
const cors = require("cors");
const {
    graphqlHTTP
} = require("express-graphql");
const {
    GraphQLObjectType,
    GraphQLInt,
    GraphQLString,
    GraphQLList,
    GraphQLSchema,
    GraphQLNonNull,
} = require("graphql");
const app = express();

app.use(cors());

let userData = [{
        id: 1,
        firstName: "John",
        lastName: "Doe",
        age: 22
    },
    {
        id: 2,
        firstName: "Mark",
        lastName: "Smith",
        age: 30
    },
    {
        id: 3,
        firstName: "Robert",
        lastName: "Hunt",
        age: 35
    },
];

const userType = new GraphQLObjectType({
    name: "User",
    description: "UserDetails",
    fields: {
        id: {
            type: GraphQLNonNull(GraphQLInt),
        },
        firstName: {
            type: GraphQLNonNull(GraphQLString),
        },
        lastName: {
            type: GraphQLNonNull(GraphQLString),
        },
        age: {
            type: GraphQLNonNull(GraphQLInt),
        },
    },
});

const rootQuery = new GraphQLObjectType({
    name: "RootQuery",
    description: "This is root query",
    fields: {
        users: {
            type: GraphQLList(userType),
            resolve: () => userData,
        },

        user: {
            type: userType,
            args: {
                id: {
                    type: GraphQLInt
                },
            },
            resolve: (_, args) => userData.find((data) => data.id === args.id),
        },
    },
});

const rootMutation = new GraphQLObjectType({
    name: "RootMutation",
    description: "This is root mutation",
    fields: {
        addUser: {
            type: userType,
            args: {
                firstName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                lastName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                age: {
                    type: GraphQLNonNull(GraphQLInt)
                },
            },
            resolve: (_, args) => {
                const newUser = {
                    id: userData.length + 1,
                    firstName: args.firstName,
                    lastName: args.lastName,
                    age: args.age,
                };
                userData.push(newUser);
                return newUser;
            },
        }
    },
});

const schema = new GraphQLSchema({
    query: rootQuery,
    mutation: rootMutation
});

app.use(
    "/graphql",
    graphqlHTTP({
        schema,
        graphiql: true,
    })
);

const PORT = 3001;

app.listen(PORT, () => {
    console.log(`Listening on port - ${PORT}`);
});

创建 app.js 文件后,现在我们需要执行npm install来安装所有必需的依赖项。成功安装所有依赖项后,我们需要运行命令npm run dev来运行 GraphQL 服务器。完成所有这些后,让我们在同一根文件夹中创建一个 Vue 应用程序。

第 3 步:创建 Vue 应用程序

这里我们使用 Vue 3 创建我们的应用程序,并确保您需要使用 v17 以下的节点版本。

Vue 3 来创建我们的应用程序

 创建 Vue 应用

现在导航到我们的应用程序并运行它。

导航到我们的应用程序

成功运行本地服务器后,它将显示如下:

Vuejs 应用主页

第 4 步:Apollo 客户端完整配置

安装这些包以集成Apollo Vue GraphQL

npm install --save @vue/apollo-option

npm install --save @apollo/client

npm install --save graphql

npm install --save graphql-tag

集成 Apollo Vue GraphQL还要安装 react 以避免出现需要安装“react”的错误。

npm install react

安装反应以避免错误上述依赖安装后的 Vue3 package.json 文件。

Vue3 package.json 文件

第 5 步:创建 Apollo Provider

配置客户端后,转到 vue 应用程序并在 src 文件夹中创建apollo.provider.js文件。

阿波罗.provider.js:

  • 这里“InMemoryCache”从“@apollo/client”加载。使用“InMemoryCache”的原因是将 API 响应存储到缓存中,以便后续请求将从缓存中加载数据,而不是一次又一次地调用 API。
  • “ApolloClient”从“@apollo/client”加载。在这里,我们必须传递诸如“缓存”和我们的 Graphql 端点之类的配置。
  • 还定义了从“@vue/apollo-option”加载的“createApolloProvider”。
import { InMemoryCache, ApolloClient } from "@apollo/client";
import { createApolloProvider } from "@vue/apollo-option";

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
  cache,
  uri: "http://localhost:3001/graphql",
});

export const provider = createApolloProvider({
defaultClient: apolloClient,
});

在 apollo.provider.js 文件中添加以上代码后,我们需要从“apollo-.provider.js”文件中导入apolloProvider并集成到 vue 实例中。

阿波罗提供商

由于我们投资于 Apollo Vue GraphQL 教程,让我们来看看需要熟悉 GraphQL 的一些关键点。

询问:

查询用于从 GraphQL 端点获取数据。

在“graphql.js”下方,getAllUsers 查询从 GraphQL 端点获取所有用户数据。

使用参数查询:

如果我们想将动态值传递给我们的查询,我们使用“变量”的概念。使用变量我们可以传递查询参数。在下面的示例中,“getSingleUser”查询“$id”是一个变量类型,其值由变量对象动态替换。

突变:

Mutation 是一种 GraphQL 操作,允许您在服务器端插入新数据或修改现有数据。您可以将 GraphQL Mutations 视为等同于 REST 中的 POST、PUT、PATCH 和 DELETE 请求。在下面的文件中,我们使用突变来添加用户。

第 6 步:通过 GraphQL 添加查询

现在,让我们在 src 文件夹中创建一个 graphql.js 文件来添加查询。为此,我们需要从“graphql-tag”导入 gql。

import agql from "graphgl-tag"; 

export const getAllUsersQuery = gql
 query users {
  users {
   id
   firstName
   lastName
   age
  }
 }
;

export const getSingleUserQuery = gql°
  query user($id: Int!) {
   user (id: $id) {
    id
    firstName
    lastName
    age
   }
  }
 ;

export const addUserMutation = gql
   mutation addUser($firstName: String!, $lastName: String!, $age: Int!) {
    addUser(firstName: $firstName, lastName: $lastName, age: $age) {
    id
    firstName
    lastName
    age
   }
  }
 ;

这样做之后,让我们看看如何在我们的组件中使用 GraphQL 查询。为此,我们假设我们有getAllUser查询,正如我们在 graphql.js 文件中定义的那样。

所有用户.vue

<template>
  <div>
    <div v-if="users">
     <div v-for="user in users'">
      {{ user.firstName }}
   </div>
  </div>
   <div v-else>No user found</div>
  </div>
</template>

为了通过 id 获取 SingleUser,我们需要在查询中提供一些参数。因此,为此让我们假设 SingleUser.vue 有一个组件。

单用户.vue

<template>
  <div>
   <div>
    <h5>{{ user.firstName + " " + user.lastName }}</h5>
     <p>Age — {{ user.age }}</p>
    </div>
  </div>
</template>

现在假设我们需要添加用户,因此我们需要从我们已经在上面的 graphql.js 文件中定义的 graphql.js 文件导入 addUserMutation。

添加用户视图

<template>
<div>
<form class="w-1/2 ml-auto mr-auto" @submit.prevent="addUser">
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model="fname" name="floating_company" id="floating_company" placeholde
</div>
</div>
<div>
<div class="relative z-@ w-full mb-6 group"
<input type="text" v-model="lname" name="floating_company" id="floating_company" placeholder="LastName" required />
</div>
</div>
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model.number="age" name="floating_company" id="floating_company" placeholder="Age" required />
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</template>

总的来说,您可以在 Vue Apollo 的帮助下使用 GraphQL 创建任何 Vue 项目。

结论

我们希望上面的 Apollo Vue GraphQL 示例教程对解决您的查询有用,并让您对使用客户端 Apollo 和 Vue 3 GraphQL 有基本的了解。要访问有关 Vue Js 的更多此类有见地的教程,请查看我们的Vue 教程

文章原文出处:https:   //www.bacancytechnology.com/

#apollo #vue #graphql 

Apollo Vue GraphQL 初学者指南
Gordon  Murray

Gordon Murray

1678338629

Beginner’s Guide with Apollo Vue GraphQL

Introduction

Vue Js is a modern javascript framework, popular for building SPAs. To make the Vue developer experience convenient, Vue Apollo client and state management library functions along with the server query language GraphQL to bring upon the formation of complex UIs.

In this blog, we have used Apollo Vue framework along with the Vue GraphQL server to execute a flawless communication. Before to start the Apollo Vue GraphQL tutorial, let’s get to the basics.

What is GraphQL?

In 2015, Facebook released GraphQL, a query language for web APIs that eases the communication between frontend and backend. It consists of schema language for server and query language for the client. Being an open specification, anyone can use it with their version framework. It can be used with any choice of backend language to create a valid GraphQL server.

Some of the popular frameworks used with GraphQL are Apollo, Hasura, GraphQL Yoga, and more. Perks of using GraphQL are that it makes the APIs fast, flexible, and developers find it easy to use.

What is Apollo?

Apollo is the most preferred GraphQL framework as it provides both frontend and backend solutions. It provides good number of tools to convert the backend as GraphQL API, and hence interact with the frontend easily.

Why the Need for Vue GraphQL?

GraphQL works for the server-side, and when the frontend wants to inquire or fetch data, the GraphQL client libraries offer solutions, and one such solution is Vue GraphQL. There are several such libraries for each frontend framework.

Need for Vue GraphQL

Installation Process of Apollo Vue GraphQL

Let us begin with the process of connecting GraphQL API to the Vue Js frontend using the Apollo client framework.

Step 1: Vue Apollo with GraphQL Server Creation

In the beginning create one project folder and inside that folder use below package.json file to create GraphQL server.

Package.json

"name": '"graphgl-server",
"version": "1.0.0",
"description": "",

"author": "",
> Debug

"scripts": {
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"main": "index.js",
"dependencies": {
    "cors": "~2.8.5",
    "express": "~4.18.2",
    "express—-graphql": "70.12.00",
   "nodemon": "2.0.20"
},
"keywords": [],
"license": "ISC"
}

Step 2: Create App.js File

After that let’s create an app.js file inside the same root folder. That app.js file is for creating schema for databases. So, you can create basic schema as per below example.

App.js

const express = require("express");
const cors = require("cors");
const {
    graphqlHTTP
} = require("express-graphql");
const {
    GraphQLObjectType,
    GraphQLInt,
    GraphQLString,
    GraphQLList,
    GraphQLSchema,
    GraphQLNonNull,
} = require("graphql");
const app = express();

app.use(cors());

let userData = [{
        id: 1,
        firstName: "John",
        lastName: "Doe",
        age: 22
    },
    {
        id: 2,
        firstName: "Mark",
        lastName: "Smith",
        age: 30
    },
    {
        id: 3,
        firstName: "Robert",
        lastName: "Hunt",
        age: 35
    },
];

const userType = new GraphQLObjectType({
    name: "User",
    description: "UserDetails",
    fields: {
        id: {
            type: GraphQLNonNull(GraphQLInt),
        },
        firstName: {
            type: GraphQLNonNull(GraphQLString),
        },
        lastName: {
            type: GraphQLNonNull(GraphQLString),
        },
        age: {
            type: GraphQLNonNull(GraphQLInt),
        },
    },
});

const rootQuery = new GraphQLObjectType({
    name: "RootQuery",
    description: "This is root query",
    fields: {
        users: {
            type: GraphQLList(userType),
            resolve: () => userData,
        },

        user: {
            type: userType,
            args: {
                id: {
                    type: GraphQLInt
                },
            },
            resolve: (_, args) => userData.find((data) => data.id === args.id),
        },
    },
});

const rootMutation = new GraphQLObjectType({
    name: "RootMutation",
    description: "This is root mutation",
    fields: {
        addUser: {
            type: userType,
            args: {
                firstName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                lastName: {
                    type: GraphQLNonNull(GraphQLString)
                },
                age: {
                    type: GraphQLNonNull(GraphQLInt)
                },
            },
            resolve: (_, args) => {
                const newUser = {
                    id: userData.length + 1,
                    firstName: args.firstName,
                    lastName: args.lastName,
                    age: args.age,
                };
                userData.push(newUser);
                return newUser;
            },
        }
    },
});

const schema = new GraphQLSchema({
    query: rootQuery,
    mutation: rootMutation
});

app.use(
    "/graphql",
    graphqlHTTP({
        schema,
        graphiql: true,
    })
);

const PORT = 3001;

app.listen(PORT, () => {
    console.log(`Listening on port - ${PORT}`);
});

After creating the app.js file now we need to execute npm install to install all required dependencies. Once installing all dependencies successfully we need to run the command npm run dev to run the GraphQL server. Once all this is done let’s create a Vue app inside the same root folder.

Step 3: Create Vue App

Here we used Vue 3 to create our app and make sure you need to use the node version below v17.

Vue 3 to create our app

 Create Vue App

Now Navigate to our app and run it.

Navigate to our app

After successfully running local server it will show like below:

Vuejs app home

Step 4: Apollo Client Full Configuration

Install these packages to integrate Apollo Vue GraphQL.

npm install --save @vue/apollo-option

npm install --save @apollo/client

npm install --save graphql

npm install --save graphql-tag

integrate Apollo Vue GraphQLAlso install react to avoid errors that says need to install “react”.

npm install react

install react to avoid errorsVue3 package.json file after above dependency installation.

Vue3 package.json file

Step 5: Create Apollo Provider

After configuring the client, go to the vue app and create apollo.provider.js file inside src folder.

apollo.provider.js:

  • Here ‘InMemoryCache’ loads from the ‘@apollo/client’. The reason behind using the ‘InMemoryCache’ is to store the API response into the cache so that the subsequent request will load the data from the cache instead of calling API again and again.
  • ‘ApolloClient’ load from the ‘@apollo/client’. Here we have to pass configurations like ‘cache’ and our Graphql endpoint.
  • Also defined the ‘createApolloProvider’ that loads from the ‘@vue/apollo-option’.
import { InMemoryCache, ApolloClient } from "@apollo/client";
import { createApolloProvider } from "@vue/apollo-option";

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
  cache,
  uri: "http://localhost:3001/graphql",
});

export const provider = createApolloProvider({
defaultClient: apolloClient,
});

After adding above code inside apollo.provider.js file we need to import apolloProvider from ‘apollo-.provider.js’ file and integrating to vue instance.

apolloProvider

As we are invested with Apollo Vue GraphQL tutorial, let us go through some key points which need to be familiar with GraphQL.

Query:

Query is used to fetch the data from GraphQL endpoint.

Below ‘graphql.js’ getAllUsers Query fetches all users data from GraphQL endpoint.

Query With Parameters:

If we want to pass dynamic value to our query we use the concept of ‘variable’. Using variables we can pass the query params. In the below example ‘getSingleUser’ query ‘$id’ is a variable type whose value is dynamically replaced by a variable object.

Mutation:

A Mutation is a GraphQL Operation that allows you to insert new data or modify the existing data on the server-side. You can think of GraphQL Mutations as the equivalent of POST, PUT , PATCH and DELETE requests in REST. In the below file we use mutation for adding a user.

Step 6: Adding Queries via GraphQL

Now, let’s create a graphql.js file inside the src folder to add queries. For that we need to import gql from ‘graphql-tag’.

import agql from "graphgl-tag"; 

export const getAllUsersQuery = gql
 query users {
  users {
   id
   firstName
   lastName
   age
  }
 }
;

export const getSingleUserQuery = gql°
  query user($id: Int!) {
   user (id: $id) {
    id
    firstName
    lastName
    age
   }
  }
 ;

export const addUserMutation = gql
   mutation addUser($firstName: String!, $lastName: String!, $age: Int!) {
    addUser(firstName: $firstName, lastName: $lastName, age: $age) {
    id
    firstName
    lastName
    age
   }
  }
 ;

After doing this let’s see how we can use GraphQL query in our component. For that let’s assume we have getAllUser query as we defined above in graphql.js file.

AllUsers.vue

<template>
  <div>
    <div v-if="users">
     <div v-for="user in users'">
      {{ user.firstName }}
   </div>
  </div>
   <div v-else>No user found</div>
  </div>
</template>

For fetching SingleUser by id we need to give some parameters inside the query. So, for that let assume one component for SingleUser.vue.

SingleUser.vue

<template>
  <div>
   <div>
    <h5>{{ user.firstName + " " + user.lastName }}</h5>
     <p>Age — {{ user.age }}</p>
    </div>
  </div>
</template>

Now let’s assume we need to add user so for that we need to import addUserMutation from graphql.js file which we already defined in above graphql.js file.

AddUser.vue

<template>
<div>
<form class="w-1/2 ml-auto mr-auto" @submit.prevent="addUser">
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model="fname" name="floating_company" id="floating_company" placeholde
</div>
</div>
<div>
<div class="relative z-@ w-full mb-6 group"
<input type="text" v-model="lname" name="floating_company" id="floating_company" placeholder="LastName" required />
</div>
</div>
<div>
<div class="relative z-0 w-full mb-6 group">
<input type="text" v-model.number="age" name="floating_company" id="floating_company" placeholder="Age" required />
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</template>

Overall, you can create any Vue project using GraphQL with the help of Vue Apollo.

Conclusion

We hope that the above Apollo Vue GraphQL sample tutorial was useful to resolve your queries, and get you the basic understanding of using client Apollo withh Vue 3 GraphQL. To get access to more such insightful tutorials on Vue Js, check out our Vue Tutorials.

Original article source at:  https://www.bacancytechnology.com/

#apollo #vue #graphql 

Beginner’s Guide with Apollo Vue GraphQL
Amos  Riva

Amos Riva

1675322620

MERN GraphQL Proyecto desde cero (usando Apollo)

En este tutorial vamos a crear un administrador de proyectos usando el Stack MERN (Mongodb, Express, React, Nodejs), usando GraphQL junto con Apollo (GraphQL Library) para poder comunicar el Backend y el Frontend. El proyecto que crearemos nos permitirá practicar con la creación de un Backend que permite ejecutar Queries y Mutations de una API de GraphQL, conectada a Mongodb y haciendo consultas que relacionen datos.

MERN GraphQL Proyecto desde cero (usando Apollo)

Índice del contenido
00:00 Introducción
02:09 Configuración del Proyecto
10:26 GraphQL Setup
30:21 Mongodb
38:50 Crear Proyecto (Mutation)
49:43 Listar Tareas (Queries)
01:04:01 Eliminar Tarea
01:12:25 Actualizar Tarea
01:27:20 Relaciones GraphQL
01:40:39 Frontend Setup
01:52:15 Listar Proyectos
02:01:40 Crear Proyecto
02:19:25 Componente de Tareas
02:35:33 Formulario de Tareas
02:47:42 Eliminar Tarea
02:53:29: TailwindCSS

Código del Ejemplo: https://github.com/fazt/mern-apollo

#graphql #mernstack #javascript #mongodb #expressjs #node #react #apollo

MERN GraphQL Proyecto desde cero (usando Apollo)
Hermann  Frami

Hermann Frami

1673548560

Lightning-fast, Globally Distributed Apollo GraphQL Server

Workers-graphql-server

An Apollo GraphQL server, built with Cloudflare Workers. Try a demo by looking at a deployed GraphQL playground.

Why this rules: Cloudflare Workers is a serverless application platform for deploying your projects across Cloudflare's massive distributed network. Deploying your GraphQL application to the edge is a huge opportunity to build consistent low-latency API servers, with the added benefits of "serverless" (I know, the project has server in it): usage-based pricing, no cold starts, and instant, easy-to-use deployment software, using Wrangler.

By the way - as a full-stack developer who loves GraphQL, and the developer advocate for Cloudflare Workers, I would love to see what you build with this! Let me know on Twitter!

Deploy to Cloudflare Workers

Usage

You can begin building your own Workers GraphQL server by installing Wrangler, the Workers command-line tool, and generating a new project:

wrangler generate my-graphql-server https://github.com/cloudflare/workers-graphql-server

You'll need to configure your project's wrangler.toml file to prepare your project for deployment. See the "Configuration" docs for a guide on how to do this. Note that you'll need to find your Cloudflare API keys to set up your config file.

The source for this project includes an example external REST data source, and defined types for the PokeAPI, as an example of how to integrate external APIs. Once you have the worker available, try this query as a sanity check:

query samplePokeAPIquery {
  pokemon: pokemon(id:1) {
    id,
    name,
    height,
    weight,
    sprites{
      front_shiny,
      back_shiny
    }
  }
}

To start using the project, configure your graphQLOptions object in src/index.js:

const graphQLOptions = {
  baseEndpoint: '/', // String
  playgroundEndpoint: '/___graphql', // ?String
  forwardUnmatchedRequestsToOrigin: false, // Boolean
  debug: false, // Boolean
  cors: true, // Boolean or Object to further configure
  kvCache: false, // Boolean
}

Endpoints

Make requests to your GraphQL server at the baseEndpoint (e.g. graphql-on-workers.signalnerve.com/) and, if configured, try GraphQL queries at the playgroundEndpoint (e.g. graphql-on-workers.signalnerve.com/___graphql).

Origin forwarding

If you run your GraphQL server on a domain already registered with Cloudflare, you may want to pass any unmatched requests from inside your Workers script to your origin: in that case, set forwardUnmatchedRequestToOrigin to true (if you're running a GraphQL server on a Workers.dev subdomain, the default of false is fine).

Debugging

While configuring your server, you may want to set the debug flag to true, to return script errors in your browser. This can be useful for debugging any errors while setting up your GraphQL server, but should be disabled on a production server.

CORS

By default, the cors option allows cross-origin requests to the server from any origin. You may wish to configure it to whitelist specific origins, methods, or headers. To do this, change the cors option to an object:

const graphQLOptions = {
  // ... other options ...

  cors: {
    allowCredentials: 'true',
    allowHeaders: 'Content-type',
    allowOrigin: '*',
    allowMethods: 'GET, POST, PUT',
  },
}

Note that by default, any field that you don't pass here (e.g. allowMethods) will fallback to the default value. See utils/setCors.js for the default values for these fields.

REST caching

Version 1.1.0 of this project includes support for caching external requests made via instances of RESTDataSource, using KV. To use caching in your project, create a new KV namespace, and in wrangler.toml, configure your namespace, calling it WORKERS_GRAPHQL_CACHE:

# wrangler.toml

[[kv-namespaces]]
binding = "WORKERS_GRAPHQL_CACHE"
id = "$myId"

With a configured KV namespace set up, you can opt-in to KV caching by changing the kvCache config value in graphQLOptions (in index.js) to true.

Download Details:

Author: Cloudflare
Source Code: https://github.com/cloudflare/workers-graphql-server 
License: MIT license

#serverless #graphql #apollo #cloudflare #worker 

Lightning-fast, Globally Distributed Apollo GraphQL Server

GraphQL Mutations and Caching using Apollo Client

In this article, we will learn GraphQL Mutations and Caching using Apollo Client in Your React App. In the previous post, we learned about configuring the Apollo Client, within our React application. We learned about how to execute queries to retrieve data from the GraphQL server. This blog post, is a continuation of the previous article. In this post, we will learn how to execute mutations using the Apollo Client to update data in the GraphQL server. Next, we will also learn about caching techniques within the Apollo Client. This article assumes that you have already read the first article in this series.

What are GraphQL Mutations?

  • Mutations can create, update and delete data.
  • Mutation fields run in series one after the other.
  • Once a mutation is complete, the Apollo cache needs to be updated.

useMutation Hook

The useMutation is a React hook, which is the API for executing GraphQL mutations in an Apollo application.

First we write the GraphQL mutation that we want to execute, using the gql symbol. Let’s take a look at an example mutation snippet below.

import { gql, useMutation } from '@apollo/client';
 const ADD_TODO = gql`
  mutation AddTodo($type: String!) {
    addTodo(type: $type) {
      id
      type
    }
  }
`;

The mutation is defined using the keyword mutation. Notice, this is similar to how we defined queries in the previous blog post. Once the mutation is defined, the useMutation hook is used to execute the mutation as follows, within a component.

const [addTodo, { data, loading, error }] = useMutation(ADD_TODO);

The useMutation hook defined above returns two items:

  • A mutate function that you can call at any time to execute the mutation.
  • An object with fields that represent the current status of the mutation’s execution.

The useMutation hook is similar to the useQuery hook. It returns the loading and error states that are tracked by the Apollo Client. This helps the frontend component display loading or error states to the user. Finally, the data property is populated when the result of the mutation comes back from the GraphQL Server.

Feature a Speaker Example

In the previous blog post, we queried the GraphQL server to display conference sessions. In this blog post, we will continue to expand upon the same conference app. We will write a mutation to feature a speaker from the conference. The featured speakers are represented with a star on the app.

You can access the code from my repository to follow along.

Code Repository: https://github.com/adhithiravi/Consuming-GraphqL-Apollo

Mutation to feature speaker:

const FEATURED_SPEAKER = gql`
  mutation markFeatured($speakerId: ID!, $featured: Boolean!) {
    markFeatured(speakerId: $speakerId, featured: $featured) {
      id
      featured
    }
  }
`;

The mutation markFeatured takes in two arguments as inputs. The SpeakerId, which cannot be null and the boolean flag for featured which can also not be null. Notice that they are both variables, and can be dynamic depending on what the client sends at a given time. The mutation then returns the fields id and featured. These fields represent the mutated values after execution.

Once the mutation is defined, we can execute the mutation within the Speakers component.

const [markFeatured, { loading, error }] = useMutation(
    FEATURED_SPEAKER
  );

To the useMutation hook, we pass the mutation name FEATURED_SPEAKER that we just defined.  The hook returns both loading and error states, along with the markFeatured mutate function.

Invoke Mutate Function

The actual mutation execution happens, when we invoke the markFeatured mutate function that is returned by the useMutation hook. In our case, we want the speaker to be marked featured, when the user clicks on the star button that is available for each speaker.

onClick={async () => {
    await markFeatured({
       variables: {
          speakerId: speakerId,
          featured: true,
       },
     });
}}

Here the onClick event of the button component, calls the mutate function markFeatured. We pass the speaker id and the featured flag to the mutate function. Putting them together, we invoke the mutate function within the star button as follows:

<button
  type="button"
  className="btn btn-default btn-lg"
  onClick={async () => {
    await markFeatured({
       variables: {
          speakerId: speakerId,
          featured: true,
       },
     });
   }}
  >
   <i
      className={`fa ${featured ? "fa-star" : "fa-star-o"}`}
      aria-hidden="true"
      style={{
         color: featured ? "gold" : undefined,
      }}
   ></i>{" "}
   Featured Speaker
 </button>

Note: Notice that with the useQuery hook, the GraphQL query is executed as soon as the component renders. Whereas with mutations, the actual execution happens only after the user action, which in turn invokes the mutate function returned by the useMutation hook.

If you are interested in the entire Speakers component code, checkout app/src/pages/conference/Speakers.jsx from Code Repo by Adhithi Ravichandran

Updating the Cache

Great! We got a hang of writing mutations with the Apollo Client.

Now let’s dive into how caching works within the Apollo Client. In our example with featured speaker, our mutation simply updates the featured boolean flag for a speaker. How do we ensure that the Apollo cache is updated after this mutation?

Let’s take a step back and ask “why do we need caching here?”. In our example app, as we click through the star icon we should instantly see the color change to yellow for the speaker we selected as featured. This would indicate that the mutation was complete. If the mutation cache was not updated, we would need to refresh the page to see the updated featured stars per speaker. Updating the local client’s cache will ensure that the UI is updated automatically, without making another network call. Hence, we need the local cache to be up-to-date.

Simple Caching

Good news is, most of the times, it is simple to update the cache, with almost no additional work.

Simply include the modified objects in the mutation response, and update the cache automatically.

In our example, the local cache is already updated after the mutation, because we have included the modified object in our mutation response as follows:

const FEATURED_SPEAKER = gql`
  mutation markFeatured($speakerId: ID!, $featured: Boolean!) {
    markFeatured(speakerId: $speakerId, featured: $featured) {
      id
      featured
    }
  }
`;

Our markFeatured mutation returns the id and featured fields. This allows the Apollo Client to normalize the objects and cache them accordingly. If the featured field was not included in the mutation response, then the local cache would not be updated with the mutated values. Simple!

Caching with Update Function

But there are times when simply including the modified objects in the mutation response is not sufficient to update the cache.

If a mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation.

In these scenarios, we would need to define an update function to apply manually update the cache after a mutate. Let’s look into another mutate example to understand this better.

Mutation to Add Session

Let’s write a new mutation to add sessions to our conference page. This mutation adds new fields, and the simple caching approach will not work to update the cache.

const CREATE_SESSION = gql`
  mutation createSession($session: SessionInput!) {
    createSession(session: $session) {
      id
      title
      startsAt
      day
      room
      level
      speakers {
        id
        name
     }   
   }
 }
`;

Update Function to Update Cache

Our mutation above creates a session. We need to define an update function which is passed to the useMutation hook as an option. This update function will manually update the sessions within the local cache. Let’s define our update function next.

const updateSessions = (cache, { data }) => {
    cache.modify({ 
      fields: {
        sessions(exisitingSessions = []) {
          const newSession = data.createSession;
          cache.writeQuery({
            query: ALL_SESSIONS,
            data: { newSession, ...exisitingSessions }
          });
        }
      }
    })
  };

  const [ create, { called, error } ] = useMutation(CREATE_SESSION, {
    update: updateSessions
  });

We pass the updateSessions function to the update option within the useMutation hook. This will ensure that once the mutation is executed, the update function is invoked. Here the updateSessions function is passed a cache object that represents the Apollo Client cache. We use the writeQuery API to access the cache. The writeQuery is used to update the cache with the updated data. The local cache will now include the existing sessions and the new session that we just created via the mutation.

The changes that you make to the cached data inside the update function are then automatically sent to the queries. Thereafter, the UI is updated with the updated cache values, without another network call.

There are also other APIs available to update the cache such as updateQuery, cache.modify, etc. To learn more about the other APIs that are available within Apollo Client to support caching, you can read the official docs linked here: Apollo Client Cache Interaction

Conclusion

Alright, that’s a wrap folks. In this post we learned about how to execute mutations using the Apollo Client to update data to the GraphQL server. We also learned about caching techniques using the Apollo Client. For further reading and resources, I have added the links below.


Original article sourced at: https://programmingwithmosh.com

#graphql #apollo #react 

GraphQL Mutations and Caching using Apollo Client

How to Test GraphQL Operation with Apollo Client

In this GraphQL article, we will learn about How to test GraphQL working with Apollo Client. learn how to test our GraphQL operations on an Apollo client in a React application.

Quality means doing it right when no one is looking! – Henry Ford

Mocked Provider

The Apollo MockedProvider is used to wrap the components tree for testing. This is similar to how we use the Provider to wrap the entire application at the root component level.

  • The MockedProvider makes the Apollo client available on React’s context.
  • It enables to define mock responses for individual queries for test.
  • Therefore, tests don’t need to interact with a real GraphQL server, instead we mock all the GraphQL data.

Testing GraphQL Queries

GraphQL Query

Here is a simple GraphQL query. It queries for conference speakers. It queries for fields such as id, name, bio, the speaker’s sessions, and so on. This is quite a straightforward GraphQL query. Now how do we write tests for a GraphQL query like this?

export const SPEAKERSQUERY = gql`
  query speakers {
    speakers {
      id
      name
      bio
      sessions {
        id
        title
      }
      featured
    }
  }
`;

Let’s create a file called Speakers.test.js, to write our test code.

Install Packages

To test our GraphQL application, we will first need to install some testing packages. In our example, I am using React DOM test utils and Enzyme to write our tests. Make sure to install the appropriate packages, before beginning the tests. The tests can also be written with Jest or other testing libraries.

Imports

import { MockedProvider } from "@apollo/client/testing";
import { mount } from "enzyme";
import { Speakers, SPEAKERSQUERY } from "./Speakers";
import React from "react";
import { act } from "react-dom/test-utils";
import wait from "waait";

Mock data for query

Our first step  to write tests here, is to define mock data for our GraphQL query. Below is the mock data, for our speakers query.

The mock data, needs to match the query and the response exactly. The mock data cannot have missing parameters.

To keep it simple, I have just mocked one speaker, and a few sessions for the speaker. You can always add more speakers and sessions to this mock response for more thorough testing.

const mockSpeakerData = {
  request: {
    query: SPEAKERSQUERY,
  },
  result: {
    data: {
      speakers: [
        {
          id: "1234",
          name: "Adhithi Ravichandran",
          bio: "Here is my bio",
          sessions: [
            {
              id: "1233",
              title: "GraphQL Testing",
            },
            {
              id: "2345",
              title: "GraphQL Big picture",
            },
          ],
          featured: false,
        },
      ],
    },
  },
};

The mock data contains a request and a result section. We can use the query parameter, and pass the SPEAKERSQUERY that we defined to the request. To the result, we pass the exact response that is expected from the GraphQL query.

Test Happy Path

We are ready to create our first test. In this test, we will pass the mock data and validate that the React component is rendered as expected.

it("renders speaker data", async () => {
    let wrapper;
    await act(async () => {
        wrapper = mount(
            <MockedProvider addTypename={false} mocks={[mockSpeakerData]}>
                <Speakers />
            </MockedProvider>
        );
    });

    await act(() => wait(0));
    wrapper.update();
    expect(wrapper).toBeTruthy();
    expect(wrapper.find(".panel-heading")).toHaveText(
        "Speaker: Adhithi Ravichandran"
    );
    expect(wrapper.find(".panel-body")).toHaveText(
        "Bio: Here is my bio"
    );
});

We are using the mount method, that remounts a component. This comes with Enzyme package. 

MockedProvider parameters:

The Speakers component is wrapped within the MockedProvider as shown above. In addition, the MockedProvider takes in some parameters as well as follows:

addTypeName: set this to false. This prevents Apollo Client from automatically adding a special typename field to every object.

mocks: Accepts the the mock data, for instance, mockSpeakerData.

wait()

We can introduce a wait, which waits for our component to update. It returns a promise that resolves after how many milliseconds you pass it.

It is perfect, for waiting any amount of time. If you do not pass it any value, it will immediately resolve.

Assertions

Once the GraphQL query operation is finished, we can assert the results. In our test, I have added expectations to find the speaker name and bio that was passed in the mocked data.

One the test is written, you can run npm test to run the test we just wrote.

Test Loading Speakers

Before the speaker data is returned as a GraphQL response, we will see a loading message on our UI. We can write a test for this loading scenario as well as follows.

it("renders loading speaker", () => {
    let wrapper;
    act(() => {
        wrapper = mount(
            <MockedProvider addTypename={false} mocks={[mockSpeakerData]}>
                <Speakers />
            </MockedProvider>
        );
    });

    expect(wrapper).toBeTruthy();
    expect(wrapper).toHaveText("Loading speakers...");
});

Notice we have removed the async/await method while making the call in this test . Since this call is going to be synchronous, the speaker data will not be returned yet, and we can expect to see the loading message instead.

Test Errors

The last test we are going to write, is one to test the error state. What if our GraphQL response returned an error? We can run into network errors or GraphQL specific errors while we try to retrieve data. To test this scenario, we will create a mock data with an error as shown below.

const mockErrorData = {
    request: {
      query: SPEAKERSQUERY,
    },
    error: new Error("Network Error"),
  };

Now, we can pass this mockErrorData  to the MockedProvider in our test.

it("renders with error", async () => {
    let wrapper;
    await act(async () => {
      wrapper = mount(
        <MockedProvider addTypename={false} mocks={[mockErrorData]}>
          <Speakers />
        </MockedProvider>
      );
    });
  
    await act(() => wait(0));
    expect(wrapper).toBeTruthy();
    expect(wrapper).toHaveText("Error loading speakers!");
  });

In this example, we have written a test to simulate network errors. Similarly, we can also write tests to validate GraphQL errors.

const mockGraphQLErrorData = {
  request: {
     query: SPEAKERSQUERY,
  },
  result: {
    errors: [new GraphQLError('Error!')],
    data: { 
    // ....
    }
  },
};

In this case, we can define an errors field, inside the mock result field. Now, it supports partial results, where the mock object can contain both errors and data.

Resources and Talk

You can find all of the code for the app and the tests that we discussed in my repo below in the Tests branch:

https://github.com/adhithiravi/Consuming-GraphqL-Apollo/tree/Tests

You can also watch my talk below at the GraphQL Summit,  which details everything we learned in this blog post:

In conclusion, testing is an important piece of software development, that is often overlooked. Let’s make sure that we focus and prioritize testing in our projects.


Original article sourced at: https://programmingwithmosh.com

#apollo #graphql 

How to Test GraphQL Operation with Apollo Client
Billy Chandler

Billy Chandler

1666339348

How to Set up a Remix Application with GraphQL

In this tutorial, you'll learn how to set up a Remix application with GraphQL functionality and create GraphQL server routes with Apollo. Learn how to implement simple CRUD functionality in a Remix app by creating GraphQL server routes with Apollo.

Remix is a great React framework with a focus on server-side rendering. Remix allows applications to have a fast load time, and once the application is loaded, hydration kicks in and gives it client-side functionality.

Since Remix can run on the server, we can create API routes in our Remix application that can perform backend tasks, like connecting to a database. Thanks to technologies and tools like Apollo GraphQL, we can utilize Remix API routes to build a functional, full-stack GraphQL application.

In this article, we will cover how to set up a Remix application with GraphQL functionality. We’ll look into how we can implement simple CRUD functionality in a Remix application by creating GraphQL server routes with Apollo.

  • What is Remix?
    • Understanding Remix API routes
  • Why use GraphQL?
    • Introduction to Apollo GraphQL
  • Overview of our Remix and GraphQL app
    • Prerequisites
  • Setting up a Remix project
  • Apollo GraphQL in Remix
  • Setting up utility functions to read and write to JSON files
    • Running our queries
    • Setting up mutations

What is Remix?

Remix is a full-stack web framework that focuses on the user interface. It works back through web fundamentals to deliver a fast, sleek, and resilient user experience.

Remix is built on React and includes React Router, server-side rendering, TypeScript support, production server, and backend optimization.

If you are familiar with React, you will know that there are several frameworks that offer server-side rendering capabilities built on React. A few such frameworks include Next.js and Astro.

Remix stands out from other server-side React frameworks for a few reasons. Firstly, unlike other frameworks like Next.js, it does not offer static site generation (SSG). Rather, it builds on the server/client model and focuses on SSR, building and compiling everything on the server and leveraging distributed systems at the edge. The client receives a smaller payload and is hydrated on the client side with React.

Remix also completely embraces web standards like the Web Fetch API, allowing developers to leverage the core tools and features the web has to offer and has developed over the years. For example, Remix leverages HTTP caching and lets the browser deal with any complexity of caching resources.

Finally, we’ll find that Remix leverages HTML <form> when it comes to data mutations and CRUD functionality, unlike other frameworks. Then, it uses actions and loaders to handle requests sent with the <form>.

Understanding Remix API routes

With Remix, routes are their own APIs. Since everything is on the server, the component gets the data on the server side when the user requests that route.

Routes in Remix are another key difference between Remix and a framework like Next.js, where the client is required to make requests to the API/server routes to perform CRUD operations with the help of loaders and actions.

Take a look at the code below:

// ./app/routes/index.tsx

import { json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

// type definitions
type Book = {
  title: string;
  genre: string;
};
type Books = Array<Book>;
type LoaderData = {
  books: Books;
};

// Loader function
export const loader = async () => {
  return json<LoaderData>({
    books: [
      {
        title: 'Harry Potter and the Deathly Hallows',
        genre: "Children's Fiction",
      },
      {
        title: "Harry Potter and the Philosopher's Stone",
        genre: "Children's Fiction",
      },
    ],
  });
};

export default function Index() {
  // use data from loader
  const { books } = useLoaderData() as LoaderData;
  return (
    <div style={{ fontFamily: 'system-ui, sans-serif', lineHeight: '1.4' }}>
      <h1>Welcome to Remix</h1>
      <ul>
        {books.map(({ title, genre }, i) => {
          return (
            <li key={i}>
              <h3> {title} </h3>
              <p> {genre} </p>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

Click here to view on StackBlitz.

In the code above, we can see we declared a loader to return an array of books that can be fetched from a remote server or database, but it’s hardcoded for now. Since loader functions are the backend “API” for our routes, we can easily get the data from the loader in our Index component with the help of useLoaderData.

Since this is all happening on the server, it is rendered and sent to the browser. There is no additional fetching done on the client side.

In addition to fetching data, we can also send data to be processed on the server side using actions.

Let’s add a form with method="post" to our Index component and an action that will handle the submission request:

import { json } from '@remix-run/node';
import { useLoaderData, useActionData, Form } from '@remix-run/react';
// type definitions
// ...

// loader function
export const loader = async () => {
 // ...
};

// action funtion
export const action = async ({ request }) => {
  const formData = await request.formData();
  const name = formData.get('name');
  return json({ name });
};

export default function Index() {
  // use data from loader
  const { books } = useLoaderData() as LoaderData;

  // get data from action
  const data = useActionData();
  return (
    <div style={{ fontFamily: 'system-ui, sans-serif', lineHeight: '1.4' }}>
      {/* show "Stranger" if no data is available yet */}
      <h1>Welcome to Remix {data ? data.name : 'Stranger'} </h1>

      <ul>
        {books.map(({ title, genre }, i) => {
          return (
            <li key={i}>
              <h3> {title} </h3>
              <p> {genre} </p>
            </li>
          );
        })}
      </ul>

      {/* Remix form component with "POST" method */}
      <Form method="post">
        <div className="form-control">
          <label htmlFor="name">
            Name
            <input id="name" name="name" type="text" />
          </label>
        </div>
        <button type="submit">Submit </button>
      </Form>
    </div>
  );
}

View on StackBlitz

In the code above, we created an action function with a request parameter. We obtain the form values by calling request.formData() and passing it to the formData variable. In order to get the name value, we call the .get() method on formData.

Lastly, we return a JSON object containing the name received from the request.

In our Index component, in order to access the JSON parsed data from our route action, we simply use the useActionData hook. It returns undefined if there hasn’t been a submission at the current location yet.

Well, that’s the most basic introduction to Remix and we’ve seen how we can get and send data in our Remix application. Next, we’ll take a look at GraphQL and how we can use it in Remix.

Why use GraphQL?

The major advantage that GraphQL has over a REST API is that GraphQL reduces the need to make more requests than is necessary when interacting with an API.

REST APIs, upon a request, tend to return more or sometimes less data than we need in our application. This might make the response from our request unnecessarily bloated, or even insufficient for an operation. We’ll then have to carry out another request, which, in turn, affects the user experience, especially in unstable network conditions).

With GraphQL, we have to ability to explicitly request what we need in a response — no more, no less.

Combining the efficiency of GraphQL with the efficiency Remix brings in building server-side rendered web applications, we’ll be looking at something truly awesome.

Introduction to Apollo GraphQL

As defined in this post, “Apollo is a suite of tools to create a GraphQL server, and to consume a GraphQL API.“

One of the tools we’ll be using is Schema Link, which allows us to perform GraphQL operations on a provided schema instead of making a network call to a GraphQL API.

This tool, which we’ll be using alongside others as we move ahead in this article, comes in pretty handy for SSR applications.

Overview of our Remix and GraphQL app

Here’s a quick rundown of what we’re going to build in this tutorial.

In the previous section, we covered routes in Remix, loader and action functions, and to demonstrate the concepts, we built a simple app that renders a list of books and includes a form that asks and displays our name upon submission.

As a reminder, you can access the code on StackBlitz and on this GitHub branch.

In the following section, we’ll be building a simple app that displays a list of books and provides a form to upload new books in a nested route, all using GraphQL queries and mutations.

You can access the final code on the schema-links branch of the repository on GitHub.

Prerequisites

To follow along with this article, we’ll need:

  • A text editor (VSCode for example)
  • Basic knowledge of Remix
  • Basic knowledge of GraphQL
  • A recent Node.js version installed

Setting up a Remix project

To create a new Remix project, run the following in the terminal:

npx create-remix@latest

Then follow the prompts:

? Where would you like to create your app? remix-graphql
? What type of app do you want to create? Just the basics
? Where do you want to deploy? Choose Remix App Server if you're unsure; it's easy to change deployment targets. Remix App Server
? TypeScript or JavaScript? TypeScript
? Do you want me to run `npm install`? Yes

Once the project has been created and installed, we can proceed.

Apollo GraphQL in Remix

In order to use Apollo GraphQL in our project, we have to install a few packages:

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

Once the packages are installed, let’s set up our GraphQL client. As aforementioned, we’ll be using Schema Link. In a new ./app/lib/apollo/index.ts file, we’ll configure our schema and resolvers:

// ./app/lib/apollo/index.ts

import { ApolloClient, gql, InMemoryCache } from "@apollo/client";
import { SchemaLink } from "@apollo/client/link/schema";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { read, write } from "../../utils/readWrite";

// a schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.
export const typeDefs = gql`

  # Comments in GraphQL strings (such as this one) start with the hash (#) symbol.
  # This "Book" type defines the queryable fields for every book in our data source.
  type Book {
    title: String
    author: String
  }

  # the "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. in this
  # case, the "books" query returns an array of zero or more Books (defined above).
  type Query {
    books: [Book]
  }
`;

// resolvers define the technique for fetching the types defined in the
// schema. this resolver retrieves books from the "books" array above.
export const resolvers = {
  Query: {
    books: () => {
      const books = read();
      return books;
    },
  }
};

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

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

In the code above, we defined our Book and Query types. For our Query, the books query returns a list of Book.

We also defined our Query resolver in resolvers, which simply returns a list of books provided by the read function. This could be a function that fetches a list of books from an external API or database. In our case, we’ll simply be getting our books from a JSON file.

Then, we create an executable schema using makeExecutableSchema and pass in the schema (typeDefs) and resolvers.

Finally, we define a new ApolloClient instance as graphQLClient and export it, ready to be used in our Remix loaders.

Before that, let’s set up our utility functions read and write to enable us to read from and modify the .json file containing our list of books.

Setting up utility functions to read and write to JSON files

Create a JSON file at ./app/data/books.json:

// ./app/data/books.json
[
  {
    "title": "The Awakening",
    "author": "Kate Chopin"
  },
  {
    "title": "City of Glass",
    "author": "Paul Auster"
  },
  {
    "title": "Harry Potter and the Deathly Hallows",
    "author": "JK Rowling"
  }
]

Create a new file ./app/utils/readWrite.ts and enter the following code:

// ./app/utils/readWrite.ts
import fs from "fs"

// JSON file containing books array
const dataPath = `${process.cwd()}/app/data/books.json`;

// function to read file contents 
export const read = (
  returnJSON = false,
  path = dataPath,
  encoding = "utf-8"
) => {
  try {
    let data = readFileSync(path, encoding);
    return returnJSON ? data : JSON.parse(data);
  } catch (error) {
    console.log({ error });
    return null;
  }
};

// function to write content to file
export const write = (data: object, path = dataPath) => {
  let initialData = read();
  let modifiedData = [...initialData, data];
  try {
    writeFileSync(path, JSON.stringify(modifiedData, null, 2));
    let result = read();
    return result;
  } catch (error) {
    console.log({ error });
    return null;
  }
};

Great! Now that our read and write functions for our resolvers have been created, let’s create a /books route to run a query that lists out all our books using the Apollo Client Schema links.

Running our queries

Create a new file ./app/routes/books.tsx with the following code:

// ./app/routes/books.tsx

import { LoaderFunction, json } from "@remix-run/node";
import { gql } from "@apollo/client";
import { graphQLClient } from "~/lib/apollo";
import { useLoaderData } from "@remix-run/react";

const query = gql`
  query GetBooks {
    books {
      title
      author
    }
  }
`;

export const loader: LoaderFunction = async ({ request, params }) => {
  const { data } = await graphQLClient.query({
    query,
  });
  return json({ books: data.books });
};

export default function Books() {
  const { books } = useLoaderData();
  return (
    <main>
      <section>
        <h1>All books</h1>
        <ul>
          {books.map(({ title, author }: { title: string; author: string }, index:number) => (
            <li key={index}>
              <h3>{title}</h3>
              <p>{author}</p>
            </li>
          ))}
        </ul>
      </section>
    </main>
  );
}

Here, we define our query and in our loader function, we execute that query using graphQLClient.query() and return the json response.

In our Books component, we get the books list using useLoaderData() and render it:

All Books UI Screen

Awesome! Our books/ route with the query works. Next, we’ll see how we can set up mutations.

Setting up mutations

First, we define our Mutation and BookInput types in typeDefs in ./app/lib/apollo/index.ts:

// ./app/lib/apollo/index.ts
// ...

export const typeDefs = gql`

  # here, we define an input
  input BookInput {
    title: String
    author: String
  }

  # here, we define our mutations
  type Mutation {
    addBook(book: BookInput): [Book]!
  }
`;

Here, we defined an input type: BookInput with title and author. They’re both strings.

We also defined an addBook mutation which accepts an argument, book, of the input type we created earlier, BookInput.

The addBook mutation then returns a list of books called [Book]

Next, we define our Mutation resolver:

// ./app/lib/apollo/index.ts

// ...
export const resolvers = {
  Query: {
   // ...
  },
  Mutation: {
    addBook: (parent: any, { book }: any) => {
      console.log({ book });
      let books = write(book);
      return books;
    },
  },
};
// ...

Here, we create a new addBook resolver that takes in book as an argument and passes it to the write() function. This adds the new book to the list and returns the updated list of books.

Create a new /books/addbook route. This is going to be a nested route, which means that we’ll have to create a books directory like ./app/routes/books/addbook.tsx:

// ./app/routes/books/addbook.tsx

import { gql } from "@apollo/client";
import { ActionFunction, json } from "@remix-run/node";
import { Form } from "@remix-run/react";
import { graphQLClient } from "~/lib/apollo";

// action function
export const action: ActionFunction = async ({ request }) => {
  const formData = await request.formData();
  const title = formData.get("title");
  const author = formData.get("author");

  let book = {
    title,
    author,
  };

  // mutation to add book
  const mutation = gql`
    mutation ($book: BookInput) {
      addBook(book: $book) {
        title
      }
    }
  `;

  const { data } = await graphQLClient.mutate({
    mutation,
    variables: { book },
  });

  return json({ books: data.books });
};
export default function AddBook() {
  return (
    <section style={{ border: "1px solid #333", padding: "1rem" }}>
      <h2>Add new book</h2>
      <Form method="post">
        <div className="form-control">
          <label htmlFor="title">Title</label>
          <input id="title" name="title" type="text" />
        </div>
        <div className="form-control">
          <label htmlFor="author">Author</label>
          <input id="author" name="author" type="text" />
        </div>
        <button type="submit">Submit</button>
      </Form>
    </section>
  );
}

Here, we can see that we have an action function where we get the title and author from the request.

Then, we create a mutation and pass the book object containing the title and author as a variable. After that, we execute this query using graphQLClient.mutate.

In our AddBook component, we use the Form component provided by Remix to send our data with method = "post".

Now, in order to render our nested route, we have to add <Outlet/> in ./app/routes/books.tsx:

// ...

export default function Books() {
  const { books } = useLoaderData();
  return (
    <main>
      <section>
        {/* ... */}
      </section>

      <Outlet />
    </main>
  );
}

Now, when we go to http://localhost:3000/books/addbook we should see this:

Working App Gif

Nice!

Conclusion

So far, we’ve been able to set Remix with Apollo GraphQL to make requests against our GraphQL schema.

We can now make queries and mutations and it all happens server-side, so there are no unnecessary network calls on our client.

This is just the tip of the iceberg of what we can achieve with Remix and GraphQL. We can also create a resource route in Remix that we can use to provide a GraphQL endpoint using Apollo Server. This will allow us to make requests against and also provide a GraphQL playground.

You can read more about resource routes in the Remix docs and also about Apollo Server in the Apollo documentation.

Original article source at https://blog.logrocket.com

#remix #apollo #graphql

How to Set up a Remix Application with GraphQL
Corey Brooks

Corey Brooks

1665978660

GraphQL API with Node, React and Apollo

Learn how to build a GraphQL API with Node, React and Apollo. Create a GraphQL API on the server in Node.js with Apollo Server. Using GraphQL on the client-side using React with Apollo Client

Learn everything you need to create a GraphQL API on the server in Node.js with Apollo Server. You'll start with a schema that includes type definitions, queries, and mutations. Then, build the resolvers needed to get the values for the API. Finally, move onto advanced features like interfaces and unions, all the way down to how relationships work in resolvers.

Learn to use GraphQL on the client-side using React with Apollo Client. In this course, you'll use queries and mutations to fetch and update GraphQL API data. You'll go further with variables, inputs, aliases on fields. Then attach your queries and mutations to React and interact with an Apollo Server using React hooks to build amazing things with React and GraphQL!

What you’ll learn:

  •        Learn everything you need to create a GraphQL API on the server in Node with Apollo Server.
  •        Learn to use GraphQL on the client-side using React with Apollo Client.

Are there any course requirements or prerequisites?

  •        Basic knowledge of javascript

Who this course is for:

  •        Developers interested in learning GraphQL

#graphql #react #node #api #apollo 

GraphQL API with Node, React and Apollo
Coding  Life

Coding Life

1664874693

User Authentication | Create Login / Register Functionality with Apollo Server V3

User Authentication with Apollo Server V3, MongoDB, GraphQL, and JWT

In this video we do an in depth setup of creating an Apollo Server capable of registering / logging in users. This login and register functionality can get tricky at times, but hopefully this video provides a great starting point for creating the backend to your applications. 

Enjoying my videos? Sign up for FREE content here: https://coopercodes.podia.com/ 

Apollo Server Setup: https://github.com/cooperlappenbusch/ApolloServerSetup 

Subscribe: https://www.youtube.com/c/CooperCodes/featured 

#mongodb #graphql #apollo 

User Authentication | Create Login / Register Functionality with Apollo Server V3

Tutorial: How to implement infinite loading with URQL GraphQL

So you're tasked with implementing an infinite loading pattern on mobile using a graphql schema. Let me share my knowledge on how to implement infinite loading on URQL on a Next.js Project. In this tutorial, I'll be showing you on how to implement infinite loading on URQL with rick and morty api as our backend endpoint

Infinite loading a is very common pattern in mobile layout since it's optimized for mobile UX as opposed to pagination which is more for desktop.  There are many ways to implement infinite loading in URQL however today I'm going to show to you probably the most simple pattern to implement infinite loading pattern using a custom hook. 

Check it out here 👉: https://reacthustle.com/blog/tutorial-how-to-implement-infinite-loading-with-urql-graphql

#nextjs  #graphql #frontend #fullstack #urql #apollo