Nextjs 使用 graphql,并且接入多个节点
写在前面
随着区块链技术的流行,也促进了 subgraph 工具的兴起。那么如何在前端接入 graphql 节点就成了关键,其接入方式既存在与 restful 接口相类似的方式,也有其独特接入风格。本文将介绍如何接入 graphql 以及如何应对多个 graphql 节点的情况。
如有不当之处,还望批评指正。
什么是 graphql
官网:https://graphql.org/
GraphQL 服务是通过定义类型和这些类型的字段来创建的,然后为每种类型的每个字段提供函数。例如,告诉您登录用户是谁(我)以及该用户名的 GraphQL 服务可能如下所示:
type Query {me: User
}type User {id: IDname: String
}
GraphQL 服务运行后(通常在 Web 服务的 URL 上),它可以接收 GraphQL 查询以进行验证和执行。服务首先检查查询以确保它仅引用定义的类型和字段,然后运行提供的函数以生成结果。
例如,查询:
{me {name}
}
可以产生以下 JSON 结果:
{"me": {"name": "My Name"}
}
接入 graphql
Fetch
由于 HTTP 的普遍性,它是使用 GraphQL 时最常见的客户端-服务器协议选择。我们可以通过 HTTP 来请求 graphql 接口。
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
。
安装依赖:
npm install @apollo/client@rc @apollo/experimental-nextjs-app-support
创建一个文件,用于随时随地获取注册完的 ApollpClient
:
// 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:
// 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 端渲染的组件中使用:
// 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 中:
// 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
获取数据即可:
"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
节点
上述讲述了通过 new HttpLink
来生成请求后端的链接,那么我们如何处理多个 api 节点时的情况呢?
多个 Link
首先仍是采用 HttpLink
生成不同 api 节点的链接:
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 来完成链接管理的所有工作:
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
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,},]),
});
完整代码
'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
就行:
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
相关文章:
Nextjs 使用 graphql,并且接入多个节点
写在前面 随着区块链技术的流行,也促进了 subgraph 工具的兴起。那么如何在前端接入 graphql 节点就成了关键,其接入方式既存在与 restful 接口相类似的方式,也有其独特接入风格。本文将介绍如何接入 graphql 以及如何应对多个 graphql 节点…...
小结——知识注入
所谓知识注入,其实不该脱离于LLM的基础工作原理,然后空谈抽象概念。 知识,也就是你问他问题,他能输出正确的回答,这只是一个简单的输出token的过程。输出得准了,就是知识,输出不准了,…...

科普文:微服务之Spring Cloud Alibaba组件Nacos一致性协议Distro+Raft概叙
一、概要 Nacos是阿里开放的一款中间件,它主要提供三种功能:持久化节点注册,非持久化节点注册和配置管理。 二、一致性协议 - AP/CP Nacos不是纯粹的AP服务,也不是纯粹的CP服务,而是两者同时支持。 这要从服务注册…...

python合并音视频-通过ffmpeg合并音视频
🌈所属专栏:【python】✨作者主页: Mr.Zwq✔️个人简介:一个正在努力学技术的Python领域创作者,擅长爬虫,逆向,全栈方向,专注基础和实战分享,欢迎咨询! 您的…...

Yolov8添加ConvNetV1和V2模块
Yolov8添加ConvNet模块 1 ConvNet系列相关内容 (1)2022 论文地址:A ConvNet for the 2020s Code Link 如下图所示,精度、效率、尺寸都很不错。 论文的摘要如下: 视觉识别的“咆哮的 20 年代”始于视觉注意力 &…...
十个常见的 Python 脚本 (详细介绍 + 代码举例)
1. 批量重命名文件 介绍: 该脚本用于批量重命名指定目录下的文件,例如将所有 ".txt" 文件重命名为 ".md" 文件。 import osdef batch_rename(directory, old_ext, new_ext):"""批量重命名文件扩展名。Args:directory: 要处理…...

【C语言】详解feof函数和ferror函数
文章目录 前言1. feof1.1 feof函数原型1.2 正确利用函数特性读写文件1.2.1 针对文本文件1.2.2 针对二进制文件 1.3 feof函数的原理1.4 feof函数实例演示 2. ferror2.1 ferror函数原型 前言 或许我们曾在网络上看过有关于feof函数,都说这个函数是检查文件是否已经读…...
ValueListenableBuilder 和 addListener 在 ChangeNotifier的区别
1、前言 ValueListenableBuilder 和 addListener 在 ChangeNotifier 中有不同的用途和用法,适用于不同的场景。它们的主要区别在于它们如何监听和响应状态变化,以及它们的用法和特性。 2、ValueListenableBuilder用法 ValueListenableBuilder 是一个 …...

ScriptEcho:AI赋能的前端代码生成神器
ScriptEcho:AI赋能的前端代码生成神器 在前端开发中,如果你总是觉得写代码太费时费力,那么 ScriptEcho 将成为你的救星。这个 AI 代码生成平台不仅能帮你省下大量时间,还能让你轻松愉快地写出生产级代码。本文将带你了解 ScriptEc…...

TypeError: ‘float’ object is not iterable 深度解析
TypeError: ‘float’ object is not iterable 深度解析与实战指南 在Python编程中,TypeError: float object is not iterable是一个常见的错误,通常发生在尝试对浮点数(float)进行迭代操作时。这个错误表明代码中存在类型使用不…...
灵茶八题 - 子序列 +w+
灵茶八题 - 子序列 w 题目描述 给你一个长为 n n n 的数组 a a a,输出它的所有非空子序列的元素和的元素和。 例如 a [ 1 , 2 , 3 ] a[1,2,3] a[1,2,3] 有七个非空子序列 [ 1 ] , [ 2 ] , [ 3 ] , [ 1 , 2 ] , [ 1 , 3 ] , [ 2 , 3 ] , [ 1 , 2 , 3 ] [1],[…...
为什么美元债务会越来越多?
美元债务规模持续膨胀,其背后原因复杂多样,可归结为以下几个主要因素: 财政赤字和刺激政策是导致美元债务增加的重要原因。美国政府长期面临财政赤字问题,支出远超收入,为弥补这一缺口,政府不得不大量发行…...
二维凸包算法 Julia实现
问题描述:给定平面上 n n n 个点的集合 Q Q Q,求其子集 P P P 构成 Q Q Q 的凸包,即 ∀ p ∈ Q , ∃ p 0 , p 1 , p 2 ∈ P \forall p \in Q, \exist p_0, p_1, p_2 \in P ∀p∈Q,∃p0,p1,p2∈P 使得点 p p p 在以点 p 0 , p 1 …...

python dash框架
Dash 是一个用于创建数据分析型 web 应用的 Python 框架。它由 Plotly 团队开发,并且可以用来构建交互式的 web 应用程序,这些应用能够包含图表、表格、地图等多种数据可视化组件。 Dash 的特点: 易于使用:Dash 使用 Python 语法…...

2.外部中断(EXTI)
理论 NVIC:嵌套向量中断控制器(解释教程) 外部通用中断线(EXTI0~EXTI15):每个GPIO设置成中断模式,与中断控制器连接的线 外部中断触发方式 上升沿触发、下降沿触发、双边沿触发 外部中断触发函数 在stm32f1xx_it.c文件…...

Python | SyntaxError: invalid syntax 深度解析
Python | SyntaxError: invalid syntax 深度解析 在Python编程中,SyntaxError: invalid syntax是一个常见的错误,它表明Python解释器在尝试解析代码时遇到了语法问题。这个错误通常是由于代码中存在拼写错误、缺少符号(如括号、冒号或逗号&a…...

付费进群系统源码原版最新修复全开源版
付费进群,和平时所见到的别人拉你进群是不一样的,付费进群需要先缴费以后,才会看到群的二维码,扫码进群或者是长按二维码图片识别进群,付费进群这个功能广泛应用于拼多多的砍价群,活动的助力群,…...
Docker容器部署的SpringBoot项目jar包,上传文件但是找不到路径的问题
在docker容器内部署的jar包运行后,请求访问都没有问题,在文件上传时,发现上传图片接口响应成功,但是图片路径报404错误,发现找不到路径。 在服务器上查看也没有找到相关图片。 原因: 启动docker镜像时没…...

云计算学习——5G网络技术
系列文章目录 提示:仅用于个人学习,进行查漏补缺使用。 Day1 网络参考模型 Day2 网络综合布线与应用 Day3 IP地址 Day4 华为eNSP网络设备模拟器的基础安装及简单使用 Day5 交换机的基本原理与配置 Day6 路由器的原理与配置 Day7 网络层协议介绍一 Day8 传…...

matlab仿真 信道编码和交织(上)
(内容源自详解MATLAB/SIMULINK 通信系统建模与仿真 刘学勇编著第八章内容,有兴趣的读者请阅读原书) clear all N10;%信息比特的行数 n7;%hamming码组长度n2^m-1 m3;%监督位长度 [H,G]hammgen(m);%产生(n,n-…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
[特殊字符] 手撸 Redis 互斥锁那些坑
📖 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作,想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁,也顺便跟 Redisson 的 RLock 机制对比了下,记录一波,别踩我踩过…...