Nextjs 使用 graphql,并且接入多个节点

embedded/2024/9/20 7:28:06/ 标签: graphql, react, javascript, nextjs, apollo/client, react.js, nodejs

写在前面

随着区块链技术的流行,也促进了 subgraph 工具的兴起。那么如何在前端接入 graphql 节点就成了关键,其接入方式既存在与 restful 接口相类似的方式,也有其独特接入风格。本文将介绍如何接入 graphql 以及如何应对多个 graphql 节点的情况。

如有不当之处,还望批评指正。

graphql_5">什么是 graphql

官网:https://graphql.org/

GraphQL 服务是通过定义类型和这些类型的字段来创建的,然后为每种类型的每个字段提供函数。例如,告诉您登录用户是谁(我)以及该用户名的 GraphQL 服务可能如下所示:

javascript">type Query {me: User
}type User {id: IDname: String
}

GraphQL 服务运行后(通常在 Web 服务的 URL 上),它可以接收 GraphQL 查询以进行验证和执行。服务首先检查查询以确保它仅引用定义的类型和字段,然后运行提供的函数以生成结果。
例如,查询:

javascript">{me {name}
}

可以产生以下 JSON 结果:

javascript">{"me": {"name": "My Name"}
}

graphql_40">接入 graphql

Fetch

由于 HTTP 的普遍性,它是使用 GraphQL 时最常见的客户端-服务器协议选择。我们可以通过 HTTP 来请求 graphql 接口。

javascript">  const data = await fetch("https://your-api-domain/graphql",{method: "POST",body: JSON.stringify({query: '{ me { name } }',}),headers: {"Content-Type": "application/json",},}).then((res) => res.json());

如示例代码所示,可以直接通过请求 graphql 的地址,其 body 则是所请求字段的 schema。

apollo-client

除了使用原生的 fetch 方法外,还可以通过市面上的工具库请求,我采用的则是 apollo/client
安装依赖:

javascript">npm install @apollo/client@rc @apollo/experimental-nextjs-app-support

创建一个文件,用于随时随地获取注册完的 ApollpClient

javascript">// lib/client.js
import { HttpLink, InMemoryCache, ApolloClient } from "@apollo/client";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";export const { getClient } = registerApolloClient(() => {return new ApolloClient({cache: new InMemoryCache(),link: new HttpLink({uri: "https://your-api-domain/graphql",}),});
});

registerApolloClient 里面判断是否存在 ApolloClient,不存在则创建一个新实例。而我们只需要通过 getClient 就能够获取 ApolloClient。

Server Side

在服务端渲染的组件中,是不允许使用 use- 的 hooks 的,因此可以这么使用 ApolloClient:

javascript">// app/page.tsx
import { getClient } from "@/lib/client";import { gql } from "@apollo/client";export const revalidate = 5;
const query = gql`query Now {now(id: "1")
}`;export default async function Page() {const client = getClient();const { data } = await client.query({ query });return <main>{data.now}</main>;
}
Client Side

那么在 Client 端,则可以使用以下步骤在 client 端渲染的组件中使用:

javascript">// lib/apollo-provider.js
"use client";import { ApolloLink, HttpLink } from "@apollo/client";
import {ApolloNextAppProvider,NextSSRInMemoryCache,NextSSRApolloClient,SSRMultipartLink,
} from "@apollo/experimental-nextjs-app-support/ssr";function makeClient() {const httpLink = new HttpLink({uri: "https://your-api-domain/graphql",});return new NextSSRApolloClient({cache: new NextSSRInMemoryCache(),link:typeof window === "undefined"? ApolloLink.from([new SSRMultipartLink({stripDefer: true,}),httpLink,]): httpLink,});
}export function ApolloWrapper({ children }: React.PropsWithChildren) {return (<ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>);
}

在 app/layout 中使用 ApolloWrapper,便可以将 Apollo 的相关数据注入到 context 中:

javascript">// app/layout.js
import { ApolloWrapper } from "/@lib/apollo-wrapper";export default function RootLayout({children,
}: {children: React.ReactNode,
}) {return (<html lang="en"><body><ApolloWrapper>{children}</ApolloWrapper></body></html>);
}

最后在任何需要请求的时候,使用 useSuspenseQuery 获取数据即可:

javascript">"use client";import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";import { gql } from "@apollo/client";const query = gql`query Now {now(id: "1")
}`;export default function Page() {const { data } = useSuspenseQuery(query);return <main>{data.now}</main>;
}

graphql__186">接入多个 graphql 节点

上述讲述了通过 new HttpLink 来生成请求后端的链接,那么我们如何处理多个 api 节点时的情况呢?

多个 Link

首先仍是采用 HttpLink 生成不同 api 节点的链接:

javascript">
const firstLink = new HttpLink({uri: 'https://your-first-api-doamin',
});
const secondLink = new HttpLink({uri: 'https://your-second-api-doamin',
});
const defaultLink = new HttpLink({ uri: 'https://your-default-api-doamin' });

拼接 Link

让我们创建一个特殊的 function 来完成链接管理的所有工作:

javascript">type LinkConditionPair = {condition: (operation: Operation) => boolean;link: HttpLink;
};function getApolloLink(pairs: LinkConditionPair[]): ApolloLink {if (pairs.length === 1) {return pairs[0].link;} else {const [firstPair, ...restPairs] = pairs;return ApolloLink.split(firstPair.condition,firstPair.link,getApolloLink(restPairs));}
}

初始化 Client

然后我们初始化 Client,将多个 api 节点传递给 NextSSRApolloClient

javascript">const client = new NextSSRApolloClient({cache: new NextSSRInMemoryCache(),link: getApolloLink([{condition: (operation: Operation) =>operation.getContext().apiName === "first",link: firstLink,},{condition: (operation: Operation) =>operation.getContext().apiName === "second",link: secondLink,},{condition: () => true,link: defaultLink,},]),
});

完整代码

javascript">'use client';import { Operation } from '@apollo/client';
import { ApolloLink, HttpLink } from '@apollo/client';
import {ApolloNextAppProvider,NextSSRApolloClient,NextSSRInMemoryCache,SSRMultipartLink,
} from '@apollo/experimental-nextjs-app-support/ssr';const firstLink = new HttpLink({uri: 'https://your-first-api-doamin',
});
const secondLink = new HttpLink({uri: 'https://your-second-api-doamin',
});
const defaultLink = new HttpLink({ uri: 'https://your-default-api-doamin' });
type LinkConditionPair = {condition: (operation: Operation) => boolean;link: HttpLink;
};function getApolloLink(pairs: LinkConditionPair[]): ApolloLink {if (pairs.length === 1) {return pairs[0].link;} else {const [firstPair, ...restPairs] = pairs;return ApolloLink.split(firstPair.condition,firstPair.link,getApolloLink(restPairs),);}
}function makeClient() {const httpLink = getApolloLink([{condition: (operation: Operation) => operation.getContext().apiName === 'first',link: firstLink,},{condition: (operation: Operation) =>operation.getContext().apiName === 'second',link: secondLink,},{condition: () => true,link: defaultLink,},]);return new NextSSRApolloClient({cache: new NextSSRInMemoryCache(),link:typeof window === 'undefined'? ApolloLink.from([new SSRMultipartLink({stripDefer: true,}),httpLink,]): httpLink,});
}export const ApolloWrapper = ({children,
}: {children: React.PropsWithChildren;
}) => {return (<ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>);
};

而我们调用请求的时候,则只需要传递 context 就行:

javascript">const { data } = useSuspenseQuery(..., { context: { apiName: 'first' } });

总结

当然,对于请求多个后端节点,我们可以简单粗暴地通过 fetch 来请求不同的后端接口实现功能,也可以声明多个 ApolloClient 实例来区分不同的后端节点。方法有很多,并没有完全的最佳解决方案。

上面是我尝试使用 apollo/client 来请求 graphql 的过程,以及通过配置 link 来请求多个后端实例的尝试。在此记录下,如有问题,还请指正。

参考:
Graphql 官网
React/Next.js: Working with multiple GraphQL endpoints and automatic type generation via Apollo Client
How to use Apollo Client with Next.js 13


http://www.ppmy.cn/embedded/90119.html

相关文章

域控搭建(windows 2012 R2和win10)

域控搭建 环境准备 两台windows虚拟机 主域控为&#xff1a;windows server2012 子域为&#xff1a;win10 虚拟机设置网段 Win10网络设置 Windows server2012网络设置 Windows server2012网络适配器 设置 识别成功 更改计算机名字 等待重启 Win10网络适配器 设置 识别成功 …

区块链的介绍和应用场景以及发展趋势

一、区块链是什么&#xff1f; &#xff08;1&#xff09;区块链的定义 区块链本质上是一个去中心化数据库。是一种分布式数据存储&#xff0c;点对点传输&#xff0c;共识机制&#xff0c;加密算法等计算机技术的新型应用模式。 误区&#xff1a;常常有人将区块链与比特币混…

学生管理系统之界面设计

学生管理系统之界面设计 建立工程 新建登录界面

opencloudosV8.6和openEuler 24安装 k8s

在三台机器上部署 Kubernetes 集群 1.环境准备2.在所有节点上进行以下步骤1. 更新系统和安装必要的软件包2. 禁用交换分区3. 禁用防火墙和SElinux4.系统主机名5.设置主机名与IP地址解析6.配置内核转发及网桥过滤7. 配置 Docker Cgroup 驱动8. 添加 Kubernetes 仓库并安装 kubea…

手机怎么设置不同的ip地址

在数字化日益深入的今天&#xff0c;智能手机已成为我们生活、工作和学习中不可或缺的设备。然而&#xff0c;随着网络应用的广泛和深入&#xff0c;我们有时需要为手机设置不同的IP地址来满足特定需求。比如&#xff0c;避免网络限制、提高网络安全、或者进行网络测试等。本文…

修改 WSL 安装的子系统的位置,节约C盘空间

问题描述 由于 WSL 玩了一阵子&#xff0c;发现C盘的磁盘空间快没了&#xff0c;如下图所示 感觉以后还是不要磁盘分区了&#xff0c;全部在C盘也没什么不好的。 挪动子系统方法 如下图所示&#xff0c;为了方便演示&#xff0c;我们安装了 ubuntu 子系统&#xff0c;并且打…

【C++】初识引用

目录 概念引用的五大特性引用在定义时必须初始化一个变量可以有多个引用一个引用可以继续有引用引用了一个实体就不能再引用另一个实体可以对任何类型做引用(包括指针) 引用使用的两种使用场景做参数交换两数单链表头结点的修改 做返回值优化传递返回值 常引用权限放大这时候进…

UWB定位技术原理及应用

UWB(超宽带)定位技术是一种基于超短脉冲信号的无线通信技术&#xff0c;具有高精度、低功耗和高安全性等特点。其主要原理是利用超短脉冲信号的时间测距&#xff0c;通过计算信号在空中飞行的时间来确定目标的位置。具体来说&#xff0c;UWB定位技术可以通过以下几种方法实现&a…

【Windows下搭建后台】Idea + Maven + JDK

Windos环境下搭建后台 一. IDEA1.1 下载1.2 安装 二. Apache Maven2.1 下载2.2 配置2.3 在IDEA中应用Maven 三. JDK3.1 下载3.2 安装2.3 配置3.4 验证3.5 在IDEA中应用 附 Windows10 IDEA ( 2024.1.4 ) Maven&#xff08;5.9.6&#xff09; JDK (11) 一. IDEA 1.1 下载 进入…

使用MySQLdump定时备份数据库实战

使用MySQLdump定时备份数据库实战 要使用mysqldump进行数据库的定时备份,你可以使用Linux系统的cron工具来设置定时任务。以下是一个简单的例子,展示如何每天凌晨1点自动备份名为mydatabase的MySQL数据库到/var/backups/mysql目录。 首先,你需要创建一个备份脚本。假设你的…

大模型LLM关键技术手段

大语言模型&#xff08;LLM&#xff09;是人工智能领域的一个突破性进展&#xff0c;它通过多种技术手段实现对自然语言的理解和生成。用比较通俗的话来列举一些我认为比较关键的技术手段&#xff1a; 深度学习技术&#xff1a;就像我们通过不断学习来掌握知识一样&#xff0c;…

云计算场景下数据恢复的挑战

基于Keepit A/S委托IDG Communications, Inc. dba Foundry进行的一项调查以及Keepit进行的深入访谈研究。这些研究揭示了数据恢复&#xff08;DR&#xff09;策略中的关键差距&#xff0c;并突出了加强数据安全措施的紧迫性。 ### 调查背景 随着云应用和生成式AI技术的迅速普及…

TongHttpServer 简介

1. 概述 随着网络技术的飞速发展,高并发大用户场景越来越普遍,单一应用服务节点已经不能满足并发需求,为了提高整个系统可靠性,扩展性,吞吐率,通常将多个应用服务器通过硬负载/软负载组成集群,负载均衡器根据不同负载算法将请求分发到各个应用服务器节点。 Tong…

《Milvus Cloud向量数据库指南》——什么是二进制嵌入?

引言 向量嵌入在现代机器学习和数据科学中已成为不可或缺的工具,它们能够将复杂数据以算法可以理解的数值格式表示。尽管密集嵌入因其能够以最小的信息损失保留语义含义而普遍存在,但随着数据量的增加,它们的计算需求和内存需求也在增加。这种增加促使开发者寻求更高效的数…

ETL工程师角度下的SQL优化

作为ETL&#xff08;Extract, Transform, Load&#xff09;工程师&#xff0c;SQL优化是提高数据处理和分析效率的关键一环。优化SQL查询可以显著降低数据处理时间&#xff0c;提高ETL过程的性能。本文将从 合理设计数据模型&#xff1a;在ETL过程中&#xff0c;正确的数据模型…

Vue-router的编程式导航有哪些方法?

Vue-router 提供了编程式导航的方法&#xff0c;这些方法允许你以编程的方式控制路由的跳转&#xff0c;而不是依赖于用户点击 <router-link> 组件。以下是 Vue-router 中常用的编程式导航方法&#xff1a; router.push(location, onComplete?, onAbort?) location&am…

机械学习—零基础学习日志(高数19——函数极限理解深化)

零基础为了学人工智能&#xff0c;真的开始复习高数 本次学习笔记&#xff0c;主要讲解函数极限的计算问题。 极限四则运算规则 这里有几个需要注意的地方。函数极限的四则运算&#xff0c;需要知道极限存在才能大胆放心的使用。而且使用超实数的概念会更好帮助我们理解&…

东方博宜1309 - 最多能倒多少杯水

问题描述 花花所在的学校引入了电水箱为同学们烧开水。 已知电水箱的容量为 n 升&#xff08; n≤10L &#xff09;&#xff0c;同学们带的杯子平均容量为 x 毫升&#xff08; x 在 100∼300 之间&#xff09;&#xff0c;请问烧一箱开水&#xff0c;最多能倒多少杯&#xff0…

fastadmin插件市场暂不可用,是否切换到本地插件

今天调试时需要安装一个富文本插件&#xff0c;结果在插件管理模块提示如下错误&#xff1a; 经过参考网上资料&#xff0c;最终解决方案&#xff1a; 修改backend/config目录下&#xff0c;fastadmin.php 中代码&#xff1a; //API接口地址 api_url > https://api.iuok.c…

【研发日记】Matlab/Simulink技能解锁(十二)——Stateflow中的两种状态机嵌套对比

文章目录 前言 项目背景 两级状态机 函数状态机 分析和应用 总结 参考资料 前言 见《【研发日记】Matlab/Simulink技能解锁(七)——两种复数移相算法》 见《【研发日记】Matlab/Simulink技能解锁(八)——分布式仿真》 见《【研发日记】Matlab/Simulink技能解锁(九)——基…