使用React和GraphQL进行CRUD:完整教程与示例
在本教程中,我们将向您展示如何使用GraphQL和React实现简单的端到端CRUD操作。我们将介绍使用React Hooks读取和修改数据的简单示例。我们还将演示如何使用Apollo Client实现身份验证、错误处理、缓存和乐观UI。
什么是React?
React是一个用于构建用户界面的JavaScript库。它旨在帮助构建应用程序的前端部分,包括处理Web和移动应用的视图层。
React是基于组件的,这意味着React应用程序的各个部分被分解成较小的组件,然后在更高级别的组件中组织。这些更高级别的组件定义了应用程序的最终结构。
React支持可重用组件,因此您可以创建一个组件,并在应用程序的不同部分多次使用。这有助于减少冗余代码,使代码更易于维护,遵循DRY原则。
什么是GraphQL?
GraphQL是一种用于API的查询语言,也是一个用现有数据来实现查询的运行时。简单来说,GraphQL是一种描述如何请求数据的语法。它通常用于从服务器加载数据到客户端。
GraphQL通过将所有请求抽象到一个端点来简化API的构建。与传统的REST API不同,它是声明式的,这意味着请求的内容会被返回。
何时使用GraphQL
当然,并不是所有项目都需要GraphQL——它只是一个用于整合数据的工具。GraphQL有定义良好的模式,因此我们可以确定不会过度获取数据。但是,如果我们已经有一个稳定的RESTful API系统,并且只依赖单一数据源的数据,那么我们不需要GraphQL。
例如,假设我们正在为自己创建一个博客,并决定在单一的MongoDB数据库中存储、检索和通信数据。在这种情况下,我们没有做任何复杂的架构设计,不需要GraphQL。
另一方面,假设我们有一个依赖多个数据源(如MongoDB、MySQL、Postgres和其他API)的完整产品。在这种情况下,我们应该使用GraphQL。
例如,如果我们在设计一个作品集网站,并希望从社交媒体和GitHub获取数据(以显示贡献),并且我们还有自己的数据库来维护博客,我们可以使用GraphQL来编写业务逻辑和模式。它将数据整合为单一的真实来源。
一旦我们有了解决函数来将正确的数据分发到前端,我们将能够轻松地在单一来源中管理数据。
什么是CRUD?
在构建API时,您希望您的模型提供四个基本功能:它应该能够创建、读取、更新和删除资源。这一组基本操作通常被称为CRUD。
RESTful API通常使用HTTP请求。在REST环境中,四个最常见的HTTP方法是GET
、POST
、PUT
和DELETE
,这是开发者可以用来创建CRUD系统的方法。
使用graphql-server
进行CRUD
在本节中,我们将介绍一些GraphQL CRUD示例,以帮助您了解在React和GraphQL应用程序中CRUD操作的工作方式。
设置服务器
我们将使用express-graphql
启动一个简单的GraphQL服务器,并将其连接到MySQL数据库。源代码和MySQL文件在这个仓库中。
GraphQL服务器是基于模式和解析器构建的。首先,我们构建一个模式(定义类型、查询、变更和订阅)。该模式描述了整个应用程序结构。
其次,对于模式中定义的内容,我们构建相应的解析器来计算和分发数据。解析器将动作映射到函数;对于在类型定义中声明的每个查询,我们创建一个解析器来返回数据。
最后,通过定义端点并传递配置来完成服务器设置。我们将/graphql
初始化为应用程序的端点。对于graphqlHTTP
中间件,我们传递构建的模式和根解析器。
除了模式和根解析器外,我们还启用了GraphiQL开发工具。GraphiQL是一个交互式的浏览器内GraphQL IDE,可以帮助我们玩转构建的GraphQL查询。
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');var schema = buildSchema(`type Query {hello: String}
`);var root = {hello: () => "World"
};var app = express();app.use('/graphql', graphqlHTTP({schema: schema,rootValue: root,graphiql: true,
}));app.listen(4000);console.log('Running a GraphQL API server at localhost:4000/graphql');
一旦服务器准备就绪,运行node index.js
将启动服务器在http://localhost:4000/graphql
。我们可以查询hello并获得字符串“World”作为响应。
连接数据库
我要建立与MySQL数据库的连接,如下所示:
var mysql = require('mysql');app.use((req, res, next) => {req.mysqlDb = mysql.createConnection({host : 'localhost',user : 'root',password : '',database : 'userapp'});req.mysqlDb.connect();next();
});
我们可以连接多个数据库/数据源,并在解析器中整合它们。我在这里连接了一个MySQL数据库。本文中使用的数据库转储在GitHub仓库中。
使用GraphQL读取和写入数据
我们使用查询和变更来读取和修改数据源
中的数据。在这个例子中,我定义了一个通用的queryDB函数来帮助查询数据库。
查询
所有的SELECT语句(或读取操作)用于列出和查看数据,放入type Query
类型定义中。我们在这里定义了两个查询:一个用于列出数据库中的所有用户,另一个用于按ID查看单个用户。
- **列出数据:**为了列出用户,我们定义了一个GraphQL模式对象类型
User
,它表示我们可以从getUsers
查询中获取或期望的内容。然后我们定义getUsers
查询来返回一个用户数组。 - **查看单个记录:**为了查看单个记录,我们使用
getUserInfo
查询定义一个参数id
。它查询数据库中的特定ID并将数据返回到前端。
现在我们已经组合了查询来获取所有记录并按ID查看记录,当我们尝试从GraphiQL查询用户时,它将在屏幕上列出一个用户数组!
查询
var schema = buildSchema(`type User {id: Stringname: Stringjob_title: Stringemail: String}type Query {getUsers: [User],getUserInfo(id: Int) : User}
`);const queryDB = (req, sql, args) => new Promise((resolve, reject) => {req.mysqlDb.query(sql, args, (err, rows) => {if (err)return reject(err);rows.changedRows || rows.affectedRows || rows.insertId ? resolve(rows) : resolve(rows[0]);});
});var root = {getUsers: (args, req) => {return queryDB(req, `SELECT * FROM user`);},getUserInfo: (args, req) => {return queryDB(req, `SELECT * FROM user WHERE id=?`, [args.id]);}
};app.use('/graphql', graphqlHTTP({schema: schema,rootValue: root,graphiql: true
}));
变更
在变更部分中,我们将执行以下操作:创建、更新和删除记录。变更按类型定义,对应于数据库中用户模式的对象类型。
type Mutation {createUser(name: String!, job_title: String!, email: String!): UserupdateUser(id: Int!, name: String, job_title: String, email: String): StringdeleteUser(id: Int!): String
}
我定义了一个带有三个参数的createUser
变更来将数据插入数据库中。updateUser
使用id
标识符来修改表中的用户记录,deleteUser
使用id
从数据库中删除记录。
const queryDB = (req, sql, args) => new Promise((resolve, reject) => {req.mysqlDb.query(sql, args, (err, rows) => {if (err)return reject(err);rows.changedRows || rows.affectedRows || rows.insertId ? resolve(rows) : resolve(rows[0]);});
});const createDB = (req, sql, args) => new Promise((resolve, reject) => {req.mysqlDb.query(sql, args, (err, rows) => {if (err)return reject(err);args[0].id = rows.insertId;resolve(args[0]);});
});var root = {getUsers: (args, req) => {return queryDB(req, `SELECT * FROM user`);},getUserInfo: (args, req) => {return queryDB(req, `SELECT * FROM user WHERE id=?`, [args.id]);},createUser: (args, req) => {return createDB(req, `INSERT INTO user SET ?`, [args]);},updateUser: (args, req) => {return queryDB(req, `UPDATE user SET ? WHERE id=?`, [args, args.id]).then((res) => "Successfully updated user").catch((err) => "Cannot update user");},deleteUser: (args, req) => {return queryDB(req, `DELETE FROM user WHERE id=?`, [args.id]).then((res) => "Successfully deleted user").catch((err) => "Cannot delete user");}
};app.use('/graphql', graphqlHTTP({schema: schema,rootValue: root,graphiql: true
}));
在React中使用graphql-client
进行CRUD
这节将使用React与GraphQL客户端整合。我们将使用Apollo客户端和GraphQL查询来从GraphQL API中读取数据并更新UI。Apollo客户端的作用类似于浏览器的Fetch API,但它是为GraphQL设计的。
设置Apollo客户端
首先,确保安装了以下包:
npm i react-apollo graphql-tag apollo-boost apollo-client apollo-cache-inmemory apollo-link-http
初始化Apollo客户端并为应用程序提供客户端对象:
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";const client = new ApolloClient({uri: "http://localhost:4000/graphql"
});ReactDOM.render(<ApolloProvider client={client}><App /></ApolloProvider>,document.getElementById("root")
);
列出数据
为了列出数据,我们首先创建一个GraphQL查询,然后将其传递给React组件。我们可以使用useQuery
钩子来执行查询并返回结果。
import React from "react";
import gql from "graphql-tag";
import { useQuery } from "react-apollo";const GET_USERS = gql`query {getUsers {idnamejob_titleemail}}
`;const UserList = () => {const { loading, error, data } = useQuery(GET_USERS);if (loading) return <p>Loading...</p>;if (error) return <p>Error :(</p>;return (<div>{data.getUsers.map(user => (<div key={user.id}><p>{user.name}</p><p>{user.job_title}</p><p>{user.email}</p></div>))}</div>);
};export default UserList;
创建用户
为了创建新用户,我们创建一个变更并将其传递给组件。我们可以使用useMutation
钩子来执行变更并返回结果。
import React from "react";
import gql from "graphql-tag";
import { useMutation } from "react-apollo";const CREATE_USER = gql`mutation CreateUser($name: String!, $job_title: String!, $email: String!) {createUser(name: $name, job_title: $job_title, email: $email) {idnamejob_titleemail}}
`;const CreateUser = () => {let name, job_title, email;const [createUser] = useMutation(CREATE_USER);return (<div><formonSubmit={e => {e.preventDefault();createUser({ variables: { name: name.value, job_title: job_title.value, email: email.value } });name.value = "";job_title.value = "";email.value = "";}}><input ref={node => { name = node; }} placeholder="Name" /><input ref={node => { job_title = node; }} placeholder="Job Title" /><input ref={node => { email = node; }} placeholder="Email" /><button type="submit">Add User</button></form></div>);
};export default CreateUser;
更新和删除用户
类似地,您可以创建更新和删除用户的组件。使用useMutation
钩子传递GraphQL变更并返回结果。
import React from "react";
import gql from "graphql-tag";
import { useMutation } from "react-apollo";const UPDATE_USER = gql`mutation UpdateUser($id: Int!, $name: String, $job_title: String, $email: String) {updateUser(id: $id, name: $name, job_title: $job_title, email: $email)}
`;const DELETE_USER = gql`mutation DeleteUser($id: Int!) {deleteUser(id: $id)}
`;const UpdateUser = () => {let id, name, job_title, email;const [updateUser] = useMutation(UPDATE_USER);return (<div><formonSubmit={e => {e.preventDefault();updateUser({ variables: { id: parseInt(id.value), name: name.value, job_title: job_title.value, email: email.value } });id.value = "";name.value = "";job_title.value = "";email.value = "";}}><input ref={node => { id = node; }} placeholder="ID" /><input ref={node => { name = node; }} placeholder="Name" /><input ref={node => { job_title = node; }} placeholder="Job Title" /><input ref={node => { email = node; }} placeholder="Email" /><button type="submit">Update User</button></form></div>);
};const DeleteUser = () => {let id;const [deleteUser] = useMutation(DELETE_USER);return (<div><formonSubmit={e => {e.preventDefault();deleteUser({ variables: { id: parseInt(id.value) } });id.value = "";}}><input ref={node => { id = node; }} placeholder="ID" /><button type="submit">Delete User</button></form></div>);
};export { UpdateUser, DeleteUser };
总结
在本教程中,我们展示了如何使用React和GraphQL进行CRUD操作。我们设置了GraphQL服务器,连接到MySQL数据库,定义了查询和变更,并使用Apollo客户端在React应用中执行CRUD操作。希望这能帮助您更好地理解和实现React和GraphQL的集成。
相关文章:

使用React和GraphQL进行CRUD:完整教程与示例
在本教程中,我们将向您展示如何使用GraphQL和React实现简单的端到端CRUD操作。我们将介绍使用React Hooks读取和修改数据的简单示例。我们还将演示如何使用Apollo Client实现身份验证、错误处理、缓存和乐观UI。 什么是React? React是一个用于构建用户…...

matplotlib 动态显示训练过程中的数据和模型的决策边界
文章目录 Github官网文档简介动态显示训练过程中的数据和模型的决策边界安装源码 Github https://github.com/matplotlib/matplotlib 官网 https://matplotlib.org/stable/ 文档 https://matplotlib.org/stable/api/index.html 简介 matplotlib 是 Python 中最常用的绘图…...

【学术小白成长之路】02三方演化博弈(基于复制动态方程)期望与复制动态方程
从本专栏开始,笔者正式研究演化博弈分析,其中涉及到双方演化博弈分析,三方演化博弈分析,复杂网络博弈分析等等。 先阅读了大量相关的博弈分析的文献,总结了现有的研究常用的研究流程,针对每个流程进行拆解。…...

短剧看剧系统投流版系统搭建,前端uni-app
目录 前言: 一、短剧看剧系统常规款短剧系统和投流版的区别? 二、后端体系 1.管理端: 2.代理投流端 三、功能区别 总结: 前言: 23年上半年共上新微短剧481部,相较于2022年全年上新的454部࿰…...
最新的ffmepg.js前端VUE3实现视频、音频裁剪上传功能
package.json "dependencies": {"ffmpeg/ffmpeg": "^0.12.10","ffmpeg/util": "^0.12.1" }vue3组件代码 根据需要更改 <script setup lang"ts"> import { FFmpeg } from ffmpeg/ffmpeg; import { fetchF…...

“Apache Kylin 实战指南:从安装到高级优化的全面教程
Apache Kylin是一个开源的分布式分析引擎,它提供了在Hadoop/Spark之上的SQL查询接口及多维分析(OLAP)能力,支持超大规模数据的亚秒级查询。以下是Kylin的入门教程,帮助您快速上手并使用这个强大的工具。 1. 安装Kylin Apache Kylin的安装是一个关键步骤,它要求您具备一…...

【iOS】内存泄漏检查及原因分析
目录 为什么要检测内存泄漏?什么是内存泄漏?内存泄漏排查方法1. 使用Zombie Objects2. 静态分析3. 动态分析方法定位修改Leaks界面分析Call Tree的四个选项: 内存泄漏原因分析1. Leaked Memory:应用程序未引用的、不能再次使用或释…...

“深入探讨Java中的对象拷贝:浅拷贝与深拷贝的差异与应用“
前言:在Java编程中,深拷贝(Deep Copy)与浅拷贝(Shallow Copy)是两个非常重要的概念。它们涉及到对象在内存中的复制方式,对于理解对象的引用、内存管理以及数据安全都至关重要。 ✨✨✨这里是秋…...

Docker 进入指定容器内部(以Mysql为例)
文章目录 一、启动容器二、查看容器是否启动三、进入容器内部 一、启动容器 这个就不多说了 直接docker run… 二、查看容器是否启动 查看正在运行的容器 docker ps查看所有的容器 docker ps -a结果如下图所示: 三、进入容器内部 通过CONTAINER ID进入到容器…...

计算机网络-数制转换与子网划分
目录 一、了解数制 1、计算机的数制 2、二进制 3、八进制 4、十进制 5、十六进制 二、数制转换 1、二进制转十进制 2、八进制转十进制 3、十六进制转十进制 4、十进制转二进制 5、十进制转八进制 6、十进制转十六进制 三、子网划分 1、IP地址定义 2、IP的两种协…...

【ssh命令】ssh登录远程服务器
命令格式:ssh 用户名主机IP # 使用非默认端口: -p 端口号 ssh changxianrui192.168.100.100 -p 1022 # 使用默认端口 22 ssh changxianrui192.168.100.100 然后输入密码,就可以登录进去了。...

【区块链】truffle测试
配置区块链网络 启动Ganache软件 使用VScode打开项目的wordspace 配置对外访问的RPC接口为7545,配置项目的truffle-config.js实现与新建Workspace的连接。 创建项目 创建一个新的目录 mkdir MetaCoin cd MetaCoin下载metacoin盒子 truffle unbox metacoincontra…...
【AIGC调研系列】chatTTS与GPT-SoVITS的对比优劣势
ChatTTS和GPT-SoVITS都是在文本转语音(TTS)领域的重要开源项目,但它们各自有不同的优势和劣势。 ChatTTS 优点: 多语言支持:ChatTTS支持中英文,并且能够生成高质量、自然流畅的对话语音[4][10][13]。细粒…...

LLVM Cpu0 新后端10
想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章: LLVM 后端实践笔记 代码在这里(还没来得及准备,先用网盘暂存一下): 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…...
k8s面试题大全,保姆级的攻略哦(二)
目录 三十六、pod的定义中有个command和args参数,这两个参数不会和docker镜像的entrypointc冲突吗? 三十七、标签及标签选择器是什么,如何使用? 三十八、service是如何与pod关联的? 三十九、service的域名解析格式…...

Mysql:通过一张表里的父子级,递归查询并且分组分级
递归函数WITH RECURSIVE语法 WITH RECURSIVE cte_name (column_list) AS (SELECT initial_query_resultUNION [ALL]SELECT recursive_queryFROM cte_nameWHERE condition ) SELECT * FROM cte_name; WITH RECURSIVE 关键字:表示要使用递归查询的方式处理数据。 c…...

数据结构之排序算法
目录 1. 插入排序 1.1.1 直接插入排序代码实现 1.1.2 直接插入排序的特性总结 1.2.1 希尔排序的实现 1.2.2 希尔排序的特性总结 2. 选择排序 2.1.1 选择排序 2.1.2 选择排序特性 2.2.1 堆排序 2.2.2 堆排序特性 3. 交换排序 3.1.1 冒泡排序 3.1.2 冒泡排序的特性 …...

移动安全赋能化工能源行业智慧转型
随着我国能源化工企业的不断发展,化工厂中经常存在火灾爆炸的危险,特别是生产场所,约有80%以上生产场所区域存在爆炸性物质。而目前我国化工危险场所移动通信设备的普及率高,但是对移动通信设备的安全防护却有所忽视,包…...

今天是放假带娃的一天
端午节放假第一天 早上5点半宝宝就咔咔乱叫了,几乎每天都这个点醒,准时的很,估计他是个勤奋的娃吧,要早起锻炼婴语,哈哈 醒来后做饭、洗锅、洗宝宝的衣服、给他吃D3,喂200ml奶粉、给他洗澡、哄睡࿰…...
linux Ubuntu安装samba服务器与SSH远程登录
目录 1,下载安装包 2,添加服务器 3,修改服务器配置 3.1 备份配置文件 3.2 修改配置 4,开启samba服务器 5,开关电脑与服务器设置 6, SSH远程登录 1,下载samba服务器安装包 sudo apt in…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解
文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...
el-amap-bezier-curve运用及线弧度设置
文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...