1682499852
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:
useReactiveVar
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.
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.
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.
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);
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' }
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.
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
1679972949
В этом руководстве мы создадим приложение CRUD с помощью React 18.2 и Apollo GraphqL 4. Это руководство предназначено для разработчиков React, которые хотят погрузиться в React graphQL . Вам нужно практическое знание React, чтобы следовать.
Чтобы выполнить этот урок наиболее эффективным способом, вам нужно помнить о некоторых вещах.
Давайте посмотрим на пример. Допустим, вашему приложению необходимо получить данные пользователя с сервера. При использовании 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-адресу дает вам интерактивную оболочку, в которой вы можете запускать тестовые запросы и мутации, прежде чем интегрировать их в свое клиентское приложение.
До сих пор мы могли настроить сервер и выполнять запросы из интерактивной оболочки. Давайте создадим приложение 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 для взаимодействия с серверной частью:
Давайте отправим некоторые данные обратно на сервер, используя мутации.
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>;
Во фрагменте кода выше:
Код также определяет 3 обработчика событий для обработки взаимодействия с пользователем:
Измените форму следующим образом:
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/
1679969002
在本教程中,我们将使用 React 18.2 和 Apollo GraphqL 4 构建一个 CRUD 应用程序,本教程面向想要深入研究 React graphQL 的React 开发人员。您需要 React 的实用知识才能跟进。
要以最有效的方式完成本教程,您需要记住一些事情。
让我们看一个例子。假设您的应用需要从服务器获取用户的详细信息。使用 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,您可以在其中运行测试查询和变更,然后再将它们集成到您的客户端应用程序中。
到目前为止,我们已经能够设置服务器并从交互式 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 的useState和input钩子,以及来自库的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 查询来与后端交互:
让我们使用突变将一些数据发送回服务器。
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>;
在上面的代码片段中:
该代码还定义了 3 个事件处理程序来处理用户交互:
像这样修改表格:
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/
1679968641
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.
To complete this tutorial in the most effective way you need to keep some things in your mind.
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.
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 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 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.
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.
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.
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/
1678346640
Vue Js — это современная среда JavaScript, популярная для создания SPA. Чтобы сделать работу разработчиков Vue удобной, клиент Vue Apollo и библиотека управления состоянием функционируют вместе с серверным языком запросов GraphQL для формирования сложных пользовательских интерфейсов.
В этом блоге мы использовали платформу Apollo Vue вместе с сервером Vue GraphQL для безупречной связи. Прежде чем приступить к руководству по Apollo Vue GraphQL , давайте перейдем к основам.
В 2015 году Facebook выпустил GraphQL , язык запросов для веб-API, упрощающий взаимодействие между интерфейсом и сервером. Он состоит из языка схемы для сервера и языка запросов для клиента. Поскольку это открытая спецификация, любой может использовать ее со своей версией фреймворка. Его можно использовать с любым языком бэкенда для создания действительного сервера GraphQL.
Некоторые из популярных фреймворков, используемых с GraphQL, — это Apollo, Hasura, GraphQL Yoga и другие. Преимущества использования GraphQL заключаются в том, что он делает API-интерфейсы быстрыми и гибкими, а разработчики находят его простым в использовании.
Apollo — наиболее предпочтительная среда GraphQL, поскольку она предоставляет как интерфейсные, так и серверные решения. Он предоставляет большое количество инструментов для преобразования серверной части в GraphQL API и, следовательно, легкого взаимодействия с интерфейсом.
GraphQL работает на стороне сервера, и когда интерфейс хочет запросить или получить данные, клиентские библиотеки GraphQL предлагают решения, и одним из таких решений является Vue GraphQL. Для каждого фреймворка фронтенда есть несколько таких библиотек.
Давайте начнем с процесса подключения GraphQL API к внешнему интерфейсу Vue Js с использованием клиентской среды 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"
}
После этого давайте создадим файл 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 в той же корневой папке.
Здесь мы использовали Vue 3 для создания нашего приложения и убедились, что вам нужно использовать версию узла ниже v17.
Теперь перейдите к нашему приложению и запустите его.
После успешного запуска локального сервера он будет выглядеть следующим образом:
Установите эти пакеты для интеграции Apollo Vue GraphQL .
npm install --save @vue/apollo-option
npm install --save @apollo/client
npm install --save graphql
npm install --save graphql-tag
Также установите реакцию, чтобы избежать ошибок, говорящих о необходимости установки «реакции».
npm install react
Файл Vue3 package.json после установки вышеуказанной зависимости.
После настройки клиента перейдите в приложение vue и создайте файл apollo.provider.js в папке src.
apollo.provider.js:
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. В приведенном ниже файле мы используем мутацию для добавления пользователя.
Теперь давайте создадим файл 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/
1678342800
Vue Js 是一个现代的 javascript 框架,在构建 SPA 方面很流行。为了方便 Vue 开发者体验,Vue Apollo 客户端和状态管理库功能与服务器查询语言 GraphQL 一起构成了复杂的 UI。
在这篇博客中,我们使用了 Apollo Vue 框架和 Vue GraphQL 服务器来执行完美的通信。在开始Apollo Vue GraphQL教程之前,让我们了解一下基础知识。
2015 年,Facebook 发布了GraphQL,这是一种用于 Web API 的查询语言,可以简化前端和后端之间的通信。它由用于服务器的模式语言和用于客户端的查询语言组成。作为一个开放规范,任何人都可以将它与他们的版本框架一起使用。它可以与任何选择的后端语言一起使用来创建有效的 GraphQL 服务器。
与 GraphQL 一起使用的一些流行框架有 Apollo、Hasura、GraphQL Yoga 等。使用 GraphQL 的好处是它使 API 快速、灵活,并且开发人员发现它易于使用。
Apollo 是最受欢迎的 GraphQL 框架,因为它同时提供前端和后端解决方案。它提供了大量工具来将后端转换为 GraphQL API,从而轻松地与前端交互。
GraphQL 服务于服务端,当前端想要查询或获取数据时,GraphQL 客户端库提供了解决方案,Vue GraphQL 就是一个这样的解决方案。每个前端框架都有几个这样的库。
让我们从使用 Apollo 客户端框架将 GraphQL API 连接到 Vue Js 前端的过程开始。
一开始创建一个项目文件夹,并在该文件夹内使用下面的 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"
}
之后让我们在同一个根文件夹中创建一个 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 应用程序。
这里我们使用 Vue 3 创建我们的应用程序,并确保您需要使用 v17 以下的节点版本。
现在导航到我们的应用程序并运行它。
成功运行本地服务器后,它将显示如下:
安装这些包以集成Apollo Vue GraphQL。
npm install --save @vue/apollo-option
npm install --save @apollo/client
npm install --save graphql
npm install --save graphql-tag
还要安装 react 以避免出现需要安装“react”的错误。
npm install react
上述依赖安装后的 Vue3 package.json 文件。
配置客户端后,转到 vue 应用程序并在 src 文件夹中创建apollo.provider.js文件。
阿波罗.provider.js:
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 请求。在下面的文件中,我们使用突变来添加用户。
现在,让我们在 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/
1678338629
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.
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.
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.
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.
Let us begin with the process of connecting GraphQL API to the Vue Js frontend using the Apollo client framework.
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"
}
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.
Here we used Vue 3 to create our app and make sure you need to use the node version below v17.
Now Navigate to our app and run it.
After successfully running local server it will show like below:
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
Also install react to avoid errors that says need to install “react”.
npm install react
Vue3 package.json file after above dependency installation.
After configuring the client, go to the vue app and create apollo.provider.js file inside src folder.
apollo.provider.js:
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.
As we are invested with Apollo Vue GraphQL tutorial, let us go through some key points which need to be familiar with GraphQL.
Query is used to fetch the data from GraphQL endpoint.
Below ‘graphql.js’ getAllUsers Query fetches all users data from GraphQL endpoint.
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.
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.
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.
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/
1675322620
Í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
1673548560
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!
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
}
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
).
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).
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.
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.
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
.
Author: Cloudflare
Source Code: https://github.com/cloudflare/workers-graphql-server
License: MIT license
1669926600
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.
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:
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.
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
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.
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
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.
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!
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.
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
}
}
}
`;
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
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
1669918860
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
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.
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.
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.
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";
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.
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.
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.
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.
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.
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.
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.
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
1666339348
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.
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 action
s and loader
s to handle requests sent with the <form>
.
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 loader
s and action
s.
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>
);
}
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.
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.
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.
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.
To follow along with this article, we’ll need:
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.
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.
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.
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:
Awesome! Our books/
route with the query works. Next, we’ll see how we can set 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:
Nice!
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
1665978660
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:
Are there any course requirements or prerequisites?
Who this course is for:
#graphql #react #node #api #apollo
1664874693
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
1664840894
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