PostgreSQL + hasura + Apollo + GraphQL + React + Antd

技术栈

PostgreSQL + hasura + Apollo + GraphQL + React + Antd

适用于复杂的查询,快速开发

环境安装

安装PostgreSQL + hasura,使用docker安装

使用 Docker Compose 部署时,它会同时启动两个容器PostgreSQLHasura GraphQL ,如下

version: "3.6"
services:postgres:image: postgres:latestcontainer_name: postgresrestart: alwaysvolumes:- ~/data/postgres:/var/lib/postgresql/dataports:- "5432:5432"environment:POSTGRES_PASSWORD: postgrespasswordgraphql-engine:image: hasura/graphql-engine:latestcontainer_name: hasuraports:- "23333:8080"depends_on:- "postgres"restart: alwaysenvironment:HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgresHASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable consoleHASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log## uncomment next line to set an admin secret# HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey

创建一个新文件夹,创建文件docker-compose.yaml复制上面内容,然后运行下面指令以安装

docker-compose up -d

安装完成后使用下面指令查看正在运行的docker

docker ps -a

在浏览器中输入localhost:23333/console可以进入Hasura的控制界面,根据上面的配置,会自动连接上数据库

PostgreSQL

数据层次

  1. Cluster (集群)
    • 集群是 PostgreSQL 实例的最高级别概念。一个集群包含多个数据库,并且所有这些数据库共享同一组配置文件、后台进程和存储区域。集群由一个特定版本的 PostgreSQL 服务器管理。
  2. Database (数据库)
    • 每个集群可以包含多个独立的数据库。每个数据库都是一个逻辑单元,拥有自己的模式(schema)、表、索引等对象。用户连接到特定的数据库进行操作,不同数据库中的对象默认情况下是隔离的。
  3. Schema (模式)
    • 模式是数据库内的命名空间,用于组织数据库对象如表、视图、函数等。每个数据库至少有一个名为 public 的默认模式,但你可以创建额外的模式来更好地组织你的数据和代码。模式有助于避免名称冲突,并允许你对数据库对象进行逻辑分组。
  4. Table (表)
    • 表是存储实际数据的地方。每个表都有一个唯一的名称(在同一模式内),并且由一组列定义,每列有其类型和约束。表可以包含零条或多条记录(行)。

创建实例数据库

CREATE SCHEMA test;
CREATE TABLE test.users (id SERIAL PRIMARY KEY,name VARCHAR(100) NOT NULL,email VARCHAR(150) UNIQUE NOT NULL
);
CREATE TABLE test.orders (id SERIAL PRIMARY KEY,user_id INT REFERENCES test.users(id) ON DELETE CASCADE,product VARCHAR(100) NOT NULL,quantity INT NOT NULL,order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 插入用户
INSERT INTO test.users (name, email)
VALUES ('Alice', 'alice@example.com'),('Bob', 'bob@example.com');-- 插入订单
INSERT INTO test.orders (user_id, product, quantity)
VALUES (1, 'Laptop', 1),(1, 'Mouse', 2),(2, 'Keyboard', 1);

hasura

然后hasura会对schema的每个表建立以下的查询方法

分别是批量查询,聚合查询以及单体查询

test_users
test_users_aggragate
test_users_by_pk    

然后我们可以通过点击需要的数据,生成对应的graphQL查询语句,如下,然后在前端使用

query MyQuery {test_users {emailnameid}
}

react

创建项目

创建新项目

npx create-react-app user-orders-app
cd user-orders-app

启动项目

npm start

appollo

安装依赖

npm install @apollo/client graphql

配置Hasura GraphQL服务器

// src/apollo-client.js
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';const client = new ApolloClient({link: new HttpLink({uri: 'http://localhost:23333/v1/graphql', // 你的 Hasura GraphQL 端点}),cache: new InMemoryCache(),
});export default client;

GraphiQL

编写graphql以直接操作数据库

// src/graphql.js
import { gql } from '@apollo/client';// 获取所有用户
export const GET_USERS = gql`query GetUsers {test_users {idnameemail}}
`;// 获取指定用户的订单
export const GET_USER_ORDERS = gql`query GetUserOrders($userId: Int!) {test_orders(where: { user_id: { _eq: $userId } }) {idproductquantityorder_date}}
`;// 创建用户
export const CREATE_USER = gql`mutation CreateUser($name: String!, $email: String!) {insert_test_users(objects: { name: $name, email: $email }) {returning {idnameemail}}}
`;// 删除用户
export const DELETE_USER = gql`mutation DeleteUser($id: Int!) {delete_test_users(where: { id: { _eq: $id } }) {returning {id}}}
`;// 更新用户
export const UPDATE_USER = gql`mutation UpdateUser($id: Int!, $name: String, $email: String) {update_test_users(where: { id: { _eq: $id } }, _set: { name: $name, email: $email }) {returning {idnameemail}}}
`;

react

编写react前端页面

// src/UserOrders.js
import React, { useState } from 'react';
import { useQuery, useMutation } from '@apollo/client';
import { GET_USERS, GET_USER_ORDERS, CREATE_USER, DELETE_USER, UPDATE_USER } from './graphql';const UserOrders = () => {const [newName, setNewName] = useState('');const [newEmail, setNewEmail] = useState('');const [updateName, setUpdateName] = useState('');const [updateEmail, setUpdateEmail] = useState('');const [selectedUserId, setSelectedUserId] = useState(null);// 获取用户列表const { loading, error, data } = useQuery(GET_USERS);// 获取指定用户的订单const { loading: ordersLoading, data: ordersData } = useQuery(GET_USER_ORDERS, {skip: !selectedUserId,variables: { userId: selectedUserId },});// 调试信息:查看获取的数据console.log('User Data:', data);console.log('Orders Data:', ordersData);// 创建用户const [createUser] = useMutation(CREATE_USER, {refetchQueries: [{ query: GET_USERS }],});// 删除用户const [deleteUser] = useMutation(DELETE_USER, {refetchQueries: [{ query: GET_USERS }],});// 更新用户const [updateUser] = useMutation(UPDATE_USER, {refetchQueries: [{ query: GET_USERS }],});const handleCreateUser = () => {createUser({ variables: { name: newName, email: newEmail } });setNewName('');setNewEmail('');};const handleDeleteUser = (id) => {deleteUser({ variables: { id } });};const handleUpdateUser = (id) => {updateUser({variables: { id, name: updateName, email: updateEmail },});setUpdateName('');setUpdateEmail('');};return (<div><h2>Create User</h2><inputtype="text"value={newName}onChange={(e) => setNewName(e.target.value)}placeholder="Name"/><inputtype="email"value={newEmail}onChange={(e) => setNewEmail(e.target.value)}placeholder="Email"/><button onClick={handleCreateUser}>Create</button><h2>Users</h2>{loading && <p>Loading users...</p>}{error && <p>Error: {error.message}</p>}{data && (<ul>{data.test_users.map((user) => (<li key={user.id}>{user.name} ({user.email})<button onClick={() => setSelectedUserId(user.id)}>View Orders</button><button onClick={() => handleDeleteUser(user.id)}>Delete</button><buttononClick={() => {setUpdateName(user.name);setUpdateEmail(user.email);handleUpdateUser(user.id);}}>Update</button></li>))}</ul>)}{selectedUserId && ordersData && (<div><h3>Orders for {data.test_users.find((user) => user.id === selectedUserId).name}</h3>{ordersLoading ? (<p>Loading orders...</p>) : (<ul>{ordersData.test_orders && ordersData.test_orders.length > 0 ? (ordersData.test_orders.map((order) => (<li key={order.id}>{order.product} - {order.quantity} (Ordered on {new Date(order.order_date).toLocaleString()})</li>))) : (<p>No orders found for this user.</p>)}</ul>)}</div>)}</div>);
};export default UserOrders;

然后再App.js中使用

// src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './apollo-client';
import UserOrders from './UserOrders';function App() {return (<ApolloProvider client={client}><div className="App"><h1>Users and Orders</h1><UserOrders /></div></ApolloProvider>);
}export default App;

antd

ant design 蚂蚁组件库,爱来自阿里,组件库,用于美化前端页面

安装

npm install antd@^4.24.2
npm install @ant-design/icons

先在index.js中引入

import 'antd/dist/antd.css';

然后对react页面应用样式

// src/UserList.js
import React, { useEffect, useState } from 'react';
import { Table, Button, Space, Modal, Form, Input, message } from 'antd';
import { useQuery, useMutation } from '@apollo/client';
import { GET_USERS, DELETE_USER, CREATE_USER, UPDATE_USER, GET_USER_ORDERS } from './graphql';// 用户列表组件
const UserList = () => {const { loading, error, data, refetch } = useQuery(GET_USERS);const [deleteUser] = useMutation(DELETE_USER);const [createUser] = useMutation(CREATE_USER);const [updateUser] = useMutation(UPDATE_USER);const [isModalVisible, setIsModalVisible] = useState(false);const [isOrdersModalVisible, setIsOrdersModalVisible] = useState(false);const [form] = Form.useForm();const [editingUser, setEditingUser] = useState(null);const [selectedUser, setSelectedUser] = useState(null);const [orders, setOrders] = useState([]);const { data: ordersData, loading: ordersLoading, error: ordersError } = useQuery(GET_USER_ORDERS, {variables: { userId: selectedUser?.id },skip: !selectedUser, // 如果没有选择用户,则跳过该查询onCompleted: (data) => setOrders(data?.test_orders || []),});// 显示删除用户的确认对话框const handleDelete = async (userId) => {try {await deleteUser({ variables: { id: userId } });message.success('User deleted successfully');refetch(); // 刷新列表} catch (err) {message.error('Failed to delete user');}};// 显示/隐藏模态框const showModal = (user) => {setEditingUser(user);form.setFieldsValue(user || { name: '', email: '' });setIsModalVisible(true);};const handleOk = async () => {try {const values = await form.validateFields();if (editingUser) {// 更新用户await updateUser({variables: { id: editingUser.id, name: values.name, email: values.email },});message.success('User updated successfully');} else {// 创建新用户await createUser({variables: { name: values.name, email: values.email },});message.success('User created successfully');}setIsModalVisible(false);refetch(); // 刷新列表} catch (err) {message.error('Failed to save user');}};const handleCancel = () => {setIsModalVisible(false);setIsOrdersModalVisible(false);};const handleUserClick = (user) => {setSelectedUser(user);setIsOrdersModalVisible(true);};const handleOrdersModalClose = () => {setSelectedUser(null);setIsOrdersModalVisible(false);};const columns = [{title: 'Name',dataIndex: 'name',key: 'name',},{title: 'Email',dataIndex: 'email',key: 'email',},{title: 'Actions',key: 'actions',render: (text, record) => (<Space size="middle"><Button type="link" onClick={() => showModal(record)}>Edit</Button><Button type="link" danger onClick={() => handleDelete(record.id)}>Delete</Button><Button type="link" onClick={() => handleUserClick(record)}>View Orders</Button></Space>),},];const orderColumns = [{title: 'Product',dataIndex: 'product',key: 'product',},{title: 'Quantity',dataIndex: 'quantity',key: 'quantity',},{title: 'Order Date',dataIndex: 'order_date',key: 'order_date',render: (date) => new Date(date).toLocaleString(),},];if (loading) return <div>Loading...</div>;if (error) return <div>Error loading users</div>;return (<div><Button type="primary" onClick={() => showModal(null)} style={{ marginBottom: 16 }}>Add User</Button><Tablecolumns={columns}dataSource={data.test_users}rowKey="id"/><Modaltitle={editingUser ? 'Edit User' : 'Create User'}visible={isModalVisible}onOk={handleOk}onCancel={handleCancel}confirmLoading={loading}><Formform={form}layout="vertical"name="userForm"><Form.Itemlabel="Name"name="name"rules={[{ required: true, message: 'Please input the name!' }]}><Input /></Form.Item><Form.Itemlabel="Email"name="email"rules={[{ required: true, message: 'Please input the email!' }, { type: 'email', message: 'Please input a valid email!' }]}><Input /></Form.Item></Form></Modal><Modaltitle={`${selectedUser?.name}'s Orders`}visible={isOrdersModalVisible}onCancel={handleOrdersModalClose}footer={null}>{ordersLoading ? (<div>Loading orders...</div>) : ordersError ? (<div>Error loading orders</div>) : (<Tablecolumns={orderColumns}dataSource={orders}rowKey="id"/>)}</Modal></div>);
};export default UserList;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482449.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目代码第1讲:各个文件夹是什么意思?按照官方文档教程创建项目,各个文件夹的理解、框架自主生成的Controller(Restful风格)

一、各个文件夹是什么意思&#xff1f; CacheHelper&#xff1a;给InMemory存储器 InMemory存储器【官方文档自带的】&#xff0c;副存储器SlaveController也没有用上 mappingProfile&#xff1a;原本想映射&#xff0c;也没用上 SelfStarting.cs&#xff1a;在桌面生成这个…

机器学习算法(六)---逻辑回归

常见的十大机器学习算法&#xff1a; 机器学习算法&#xff08;一&#xff09;—决策树 机器学习算法&#xff08;二&#xff09;—支持向量机SVM 机器学习算法&#xff08;三&#xff09;—K近邻 机器学习算法&#xff08;四&#xff09;—集成算法 机器学习算法&#xff08;五…

docker安装hadoop环境

一、使用docker搭建基础镜像 1、拉取centos系统镜像 # 我这里使用centos7为例子 docker pull centos:7 2、创建一个dockerfiler文件&#xff0c;用来构建自定义一个有ssh功能的centos镜像 # 基础镜像 FROM centos:7 # 作者 #MAINTAINER hadoop ADD Centos-7.repo /etc/yum.re…

vue 2 父组件根据注册事件,控制相关按钮显隐

目标效果 我不注册事件&#xff0c;那么就不显示相关的按钮 注册了事件&#xff0c;才会显示相关内容 实现思路 组件在 mounted 的时候可以拿到父组件注册监听的方法 拿到这个就可以做事情了 mounted() {console.log(this.$listeners, this.$listeners);this.show.search !…

第29天 MCU入门

目录 MCU介绍 MCU的组成与作用 电子产品项目开发流程 硬件开发流程 常用元器件初步了解 硬件原理图与PCB板 常见电源符号和名称 电阻 电阻的分类 贴片电阻的封装说明&#xff1a; 色环电阻的计算 贴片电阻阻值计算 上拉电阻与下拉电阻 电容 电容的读数 二极管 LED 灯电路 钳位作…

王道考研编程题总结

我还在完善中&#xff0c;边复习边完善&#xff08;这个只是根据我自身总结的&#xff09; 一、 线性表 1. 结构体 #define MaxSize 40 typedef struct{ElemType data[MaxSize]&#xff1b;int length; }SqList 2. 编程题 1. 删除最小值 题意 &#xff1a;从顺序表中删除…

PHM技术:一维信号时序全特征分析(统计域/频域/时域)| 信号处理

目录 0 引言 1 基于统计域的时序特征分析 2 基于谱域的时序特征分析 3 基于时域的时序特征分析 4 代码分析 5 小结 0 引言 我们将探索时序特征分析&#xff0c;这是信号处理中的关键技术之一。信号在我们的日常生活中无处不在&#xff0c;从声音到图像&#xff0c;从传感…

无人机数据处理系统:原理与核心系统

一、数据处理系统的运行原理 数据获取&#xff1a;无人机在飞行过程中&#xff0c;通过搭载的传感器&#xff08;如相机、激光雷达等&#xff09;采集到各种类型的数据&#xff0c;例如图像、点云等。这些数据是后续处理和分析的基础。 数据传输&#xff1a;采集到的数据会通…

close and shutdown?

背景&#xff1a;我们要讲述的是网络编程中常用的两个API&#xff1a; #include <unistd.h> int close(int fd); #include <sys/socket.h> int shutdown(int sockfd, int how); 以及TCP的半连接&#xff0c;半打开。 shutdown函数的行为依赖第二个参数区分&#xf…

Java设计模式——职责链模式:解锁高效灵活的请求处理之道

嘿&#xff0c;各位 Java 编程大神和爱好者们&#xff01;今天咱们要一同深入探索一种超厉害的设计模式——职责链模式。它就像一条神奇的“处理链”&#xff0c;能让请求在多个对象之间有条不紊地传递&#xff0c;直到找到最合适的“处理者”。准备好跟我一起揭开它神秘的面纱…

Javascript中DOM事件监听 (鼠标事件,键盘事件,表单事件)

#DOM&#xff08;Document Object Model&#xff09;事件监听是一种机制&#xff0c;它允许 JavaScript 代码在 HTML 文档中的元素上监听特定的事件。当这些事件发生时&#xff0c;与之关联的 JavaScript 函数&#xff08;也称为事件处理函数&#xff09;就会被执行。这使得网页…

TiDB 无统计信息时执行计划如何生成

作者&#xff1a; weiyinghua 原文来源&#xff1a; https://tidb.net/blog/4c49ac0d 一、Pseudo 统计信息总体生成规则 TiDB 在表无统计信息时&#xff0c;不会进行动态采样&#xff0c;而是用静态的、预设规则以及经验假设来生成计划。用函数 PseudoTable 创建一个伪统…

服务器密码错误被锁定怎么解决?

当服务器密码错误多次导致账号被锁定时&#xff0c;解决方法需要根据服务器的操作系统&#xff08;如 Linux 或 Windows &#xff09;和具体服务器环境来处理。以下是常见的解决办法&#xff1a; 一、Linux 服务器被锁定的解决方法 1. 使用其他用户账号登录 如果有其他未被…

认识redis 及 Ubuntu安装redis

文章目录 一. redis概念二. redis应用场景二. redis的特性四. 使用Ubuntu安装redis 一. redis概念 redis 是在内存中存储数据的中间件, 用在分布式系统 redis是客户端服务器结构的程序, 客户端服务器之间通过网络来通信 二. redis应用场景 redis可用作数据库 类似MySQL, 但…

LabVIEW内燃机气道试验台测控系统

基于LabVIEW软件开发的内燃机气道试验台测控系统主要应用于内燃机气道的性能测试和数据分析&#xff0c;通过高精度的测控技术&#xff0c;有效提升内燃机的测试精度和数据处理能力。 项目背景 随着内燃机技术的发展&#xff0c;对其气道性能的精准测量需求日益增加。该系统通…

Rust vs Java:后端开发应该选哪个?

后端技术的发展迅速。根据JetBrains 2024年开发者调查,尽管Java仍然占据约34.5%的市场份额,但Rust在高性能应用中的应用逐渐增多。过去四年中,Rust在企业中的采用增长了240%(根据Stack Overflow 2024开发者调查)。随着组织更加注重效率和可扩展性,选择Rust还是Java已成为…

触觉智能亮相OpenHarmony人才生态大会2024

11月27日&#xff0c;OpenHarmony人才生态大会2024在武汉隆重举行。本次大会汇聚了政府领导、学术大咖、操作系统技术专家、高校及企业代表&#xff0c;围绕新时代背景下的操作系统人才培养进行了深入探讨&#xff0c;分享高校、企业在产学研融合方面的先进经验&#xff0c;全面…

springboot366高校物品捐赠管理系统(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 高校物品捐赠管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff…

深入理解AIGC背后的核心算法:GAN、Transformer与Diffusion Models

深入理解AIGC背后的核心算法&#xff1a;GAN、Transformer与Diffusion Models 前言 随着人工智能技术的发展&#xff0c;AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;已经不再是科幻电影中的幻想&#xff0c;而成为了现实生活中的一种新兴力…

企业网站面临的爬虫攻击及安全防护策略

在当今数字化时代&#xff0c;企业网站不仅是展示企业形象的窗口&#xff0c;更是进行商业活动的重要平台。然而&#xff0c;企业网站在日常运营中面临着多种类型的爬虫攻击&#xff0c;这些攻击不仅会对网站的正常访问造成影响&#xff0c;还可能窃取敏感数据&#xff0c;给企…