加入我们,10月8日至10日在纽约市,学习关于 GraphQL 联邦和 API 平台工程的最新技巧、趋势和新闻。加入我们,参加 2024 年 NYC 的 GraphQL 大会
文档
免费开始

Apollo Link 概述

定制 Apollo 客户端的数据流


如果你的应用程序只需要向一个发送常规基于HTTP的请求,你可能不需要使用 API。请参见基本HTTP网络

Apollo Link库帮助您自定义 Apollo 客户端与您的 GraphQL 服务器之间的数据流。您可以定义客户端的网络行为为一个链式的link对象,按顺序执行:

Apollo Client
Initiated
down
down
up
up
Completed
Request
Response
GraphQL operation
Link
Link
Terminating Link
GraphQL server

每个链应该代表对 操作的自我包含修改或者副作用(例如日志记录)。

在上面的图中

  1. 第一个链可能为了调试目的记录操作详情。
  2. 第二个链可能为了认证目的向出站的操作请求中添加HTTP头部。
  3. 最后的(终止的)链将操作发送到目的地(通常是HTTP的 GraphQL 服务器)。
  4. 服务器的响应将按逆序传回每个链,这样链就可以修改响应或在实际数据被缓存前采取其他行动。

默认情况下,Apollo 客户端使用 Apollo Link HttpLink用于发送GraphQL操作到远程服务器通过HTTP。Apollo Client负责创建此默认链,并且无需额外自定义即可覆盖许多用例。

要扩展或替换这种默认的网络行为,您可以在ApolloClient构造函数中定义自定义链并指定它们的执行顺序。

以下示例展示了包含两个Apollo提供的链接的基本链接链

  • 一个onError链接,它会检查服务器响应中的错误。它会记录找到的任何错误详情。
  • 一个HttpLink将每个GraphQL操作发送到您的服务器。

请注意,如果您向ApolloClient构造函数提供链接链,则不要提供uri选项。相反,您需要将服务器的URL提供到您的HttpLink中。

链接对象是ApolloLink(或其子类)的实例。每个链接都必须定义一个名为request的方法,这被称为链接的请求处理程序。您可以将此方法的定义提供给ApolloLink构造函数。

示例

以下自定义链接定义了一个请求处理器,该处理器将 GraphQL 操作的近似开始时间添加到操作的 上下文:

import { ApolloLink } from '@apollo/client';
const timeStartLink = new ApolloLink((operation, forward) => {
operation.setContext({ start: new Date() });
return forward(operation);
});

然后此请求处理器调用 return forward(operation),这是调用链中下一个链接语法的句法。

请求处理器

每个链接必须定义一个 request 方法,也称为它的 请求处理器。 此方法传入以下 :

  • operation:正在通过链接传递的 GraphQL 操作。
  • forward:一个用于执行链中下一个链接的函数(除非这是一个 终止链接)。

每当 Apollo Client 准备执行 GraphQL 操作时,它都会调用链中第一个链接的请求处理器。每个链接都负责执行其逻辑,然后通过调用 forward 函数并返回其结果来将执行传递给下一个链接。

Operation 对象

The Operation 对象包括以下 :

名称说明
查询一个 DocumentNode(解析的 GraphQL 操作),描述正在进行的操作。
变量一个映射,携带与操作一起发送的 GraphQL 变量。
operationName如果有命名,则为查询的字符串名称,否则 null
扩展一个映射,用于存储要发送到服务器的扩展数据。
getContext一个用于返回请求上下文的函数。此上下文可以由链接用于确定要执行哪些操作。请参阅 管理上下文
setContext一个函数,它接受一个新的上下文对象,或者一个函数,该函数接受先前的上下文并返回一个新对象。请参阅 管理上下文

forward 函数

当您的自定义链接的请求处理器执行完其逻辑后,它应该返回一个对传入的 forward 函数的调用(除非它是链的 终止链接)。调用 return forward(operation)会将执行传递给链中的下一个链接。

如果一个非终止的自定义链接的请求处理器 return forward(operation),则链接链终止,并且相关的 GraphQL 操作不会执行。

前进函数的返回类型是由 zen-observable 库提供的一个 Observable 类型。有关 zen-observable 的详细信息,请参阅其文档。

处理响应

当您的 GraphQL 服务器对操作返回结果时,该结果会在缓存之前,通过链中的每个链接传递上去:

Apollo Client
Initiated
down
down
up
up
Completed
Request
Response
GraphQL operation
Link
Link
Terminating Link
GraphQL server

每个链接可以通过修改其对请求处理者的 return 语句来执行逻辑,如下所示:

// BEFORE (NO INTERACTION)
return forward(operation);
// AFTER
return forward(operation).map((data) => {
// ...modify result as desired here...
return data;
});

传给 map 的函数返回的任何内容都会传递到链中的下一个链接。

在这里也可以执行与结果无关的逻辑。此请求处理程序使用 请求上下文 来估计每个操作往返延时:

import { ApolloLink } from '@apollo/client';
const roundTripLink = new ApolloLink((operation, forward) => {
// Called before operation is sent to server
operation.setContext({ start: new Date() });
return forward(operation).map((data) => {
// Called after server responds
const time = new Date() - operation.getContext().start;
console.log(`Operation ${operation.operationName} took ${time} to complete`);
return data;
});
});

每个链接代表对 GraphQL 操作的独立修改或副作用(如日志记录)。通过将这些链接组合成链,您可以创建任意复杂的数据流模型。

链接有两种组合形式:累加和方向性。

  • 累加组合 涉及将一组链接组合成串联执行的链:

    Link
    Link
    Terminating Link

  • 方向性组合 根据操作的详细信息决策分支到一个链接或另一个链接:

    Link
    Link
    Terminating Link
    Terminating Link

请注意,无论链如何分支,每个分支最终都会以一个 终止链接 结尾。

终止链接是链接链中的最后一个链接。它不调用 终止链接 中的 forward 函数,而是负责将组合的 GraphQL 操作发送到你执行它的目的地(通常是 GraphQL 服务器),并返回一个 ExecutionResult

HttpLinkBatchHttpLink 都是终止链接的示例。

累加组合

如果您有一组两个或更多应按顺序执行的链接,请使用 ApolloLink.from 辅助方法将那些链接组合成一个 单个 链接,如下所示:

import { from, HttpLink } from '@apollo/client';
import { RetryLink } from '@apollo/client/link/retry';
import MyAuthLink from '../auth';
const additiveLink = from([
new RetryLink(),
new MyAuthLink(),
new HttpLink({ uri: 'https://127.0.0.1:4000/graphql' })
]);

方向性组合

您可能希望链的执行根据操作的详细信息进行分支。

为了支持这一点,@apollo/client 库提供了一个 split 函数,允许您根据布尔检查的结果使用两个不同的 Link。您还可以使用 ApolloLink 实例的 split 方法。

名称说明
测试一个函数,接收当前的 Operation,并根据其详细信息返回 truefalse
如果测试函数返回 true,则执行下一个链接。
一个 可选 的链接,如果测试函数返回 false,则执行下一个链接。如果没有提供,则使用请求处理器的 forward 参数。

在以下示例中,一个 RetryLink 会根据相关上下文的 version 将执行传递给两个不同的 HttpLink

import { ApolloLink, HttpLink } from '@apollo/client';
import { RetryLink } from '@apollo/client/link/retry';
const directionalLink = new RetryLink().split(
(operation) => operation.getContext().version === 1,
new HttpLink({ uri: 'https://127.0.0.1:4000/v1/graphql' }),
new HttpLink({ uri: 'https://127.0.0.1:4000/v2/graphql' })
);

split 方法的其他用途包括:

  • 根据操作类型自定义允许的重试尝试次数
  • 根据操作类型使用不同的传输方法(例如,查询使用 HTTP 而订阅使用 WebSocket)
  • 根据用户是否登录自定义逻辑

在以下示例中,所有 操作都发送到 GraphQLWsLink,所有其他操作都发送到 HttpLink

import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
export const link = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
new GraphQLWsLink(createClient({ url: 'ws://127.0.0.1:3000/subscriptions' })),
new HttpLink({ uri: 'https://127.0.0.1:4000/graphql' })
);

提供 Apollo Client

在您完成整个链接链的构建之后,将结果链接提供给 ApolloClient 构造函数,如下所示:

import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import { RetryLink } from '@apollo/client/link/retry';
const directionalLink = new RetryLink().split(
(operation) => operation.getContext().version === 1,
new HttpLink({ uri: "https://127.0.0.1:4000/v1/graphql" }),
new HttpLink({ uri: "https://127.0.0.1:4000/v2/graphql" })
);
const client = new ApolloClient({
cache: new InMemoryCache(),
link: directionalLink
});

大多数链接为每个 operation 执行相同的逻辑,并且它们不需要了解之前执行的 operation。这些链接是 无状态的

您可以在 ApolloLink 对象的构造函数中定义无状态链接的请求处理器,如下所示:

import { ApolloLink } from '@apollo/client';
const consoleLink = new ApolloLink((operation, forward) => {
console.log(`starting request for ${operation.operationName}`);
return forward(operation).map((data) => {
console.log(`ending request for ${operation.operationName}`);
return data;
})
})

无状态链接非常适合实现中间件甚至网络请求。以下链接为每个出去的请求添加了一个 Authorization 头:

import { ApolloLink } from '@apollo/client';
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers }) => ({ headers: {
authorization: Auth.userId(), // however you get your token
...headers
}}));
return forward(operation);
});

这种风格的链接也适用于函数的定制

import { ApolloLink } from '@apollo/client';
const reportErrors = (errorCallback) => new ApolloLink((operation, forward) => {
return new Observable((observer) => {
const observable = forward(operation);
const subscription = observable.subscribe({
next(value) {
observer.next(value);
},
error(networkError) {
errorCallback({ networkError, operation });
observer.error(networkError);
},
complete() {
observer.complete();
},
});
return () => subscription.unsubscribe();
});
});
const link = reportErrors(console.error);

您还可以通过扩展 ApolloLink 类和覆盖其构造函数和请求处理器来创建无状态链接。例如,以下是将相同的 reportErrors 链接编写为 ApolloLink 扩展的示例:

import { ApolloLink } from '@apollo/client';
class ReportErrorLink extends ApolloLink {
constructor(errorCallback) {
super();
this.errorCallback = errorCallback;
}
request(operation, forward) {
const observable = forward(operation);
// errors will be sent to the errorCallback
observable.subscribe({ error: this.errorCallback })
return observable;
}
}
const link = new ReportErrorLink(console.error);

有用时,链接可以在 operation 之间维护状态。这些链接是 有状态的

有状态的链接通常被定义为 ApolloLink 的子类。它们覆盖了 ApolloLink 的构造函数,并实现了一个具有与无状态链接相同签名的 request 函数。例如:

import { ApolloLink } from '@apollo/client';
class OperationCountLink extends ApolloLink {
constructor() {
super();
this.operationCount = 0;
}
request(operation, forward) {
this.operationCount += 1;
return forward(operation);
}
}
const link = new OperationCountLink();

这个有状态的链接维护一个名为 operationCount 的计数器作为实例变量。每当请求穿过链接时,operationCount 就会增加。

管理上下文

作为一个操作沿着你的链接链移动时,它会维护一个 context,每个链接都可以读取和修改。这使得链接可以在链中传递元数据,其他链接可以在它们的执行逻辑中使用这些元数据。

  • 通过调用 operation.getContext() 来获取当前上下文对象。
  • 修改上下文对象,然后通过 operation.setContext(newContext)operation.setContext((prevContext) => newContext) 将其写回。

请注意,这个上下文不会被包含在终止链接发给 GraphQL 服务器或其他目的地的请求中。

以下是一个示例

import { ApolloLink, from } from '@apollo/client';
const timeStartLink = new ApolloLink((operation, forward) => {
operation.setContext({ start: new Date() });
return forward(operation);
});
const logTimeLink = new ApolloLink((operation, forward) => {
return forward(operation).map((data) => {
// data from a previous link
const time = new Date() - operation.getContext().start;
console.log(`operation ${operation.operationName} took ${time} to complete`);
return data;
})
});
const additiveLink = from([
timeStartLink,
logTimeLink
]);

这个示例定义了两个链接,timeStartLinklogTimeLink。链接 timeStartLink 将当前时间赋值到上下文的 start 字段。操作完成时,链接 logTimeLink 从当前时间减去 start 的值,以确定操作的总持续时间。

你可以通过向 useQuery 钩子 (或 useMutationuseSubscription 等)提供 context 参数来设置特定操作的上下文对象的初始值。

上一页
HOC(已弃用)
下一页
HTTP
评价文章评价在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,商业用名 Apollo GraphQL。

隐私政策

公司