# @apollo/datasource-rest 中文文档

规范地址：https://npmcn.com/package/%40apollo/datasource-rest/

版本：6\.4\.1
npm 元数据更新时间：2026-01-22T17:29:57.125Z
中文译文更新时间：2026-11-06T10:59:19.793Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40apollo/datasource-rest
原文对照：https://npmcn.com/package/%40apollo/datasource-rest/?lang=original
项目主页：https://github.com/apollographql/datasource-rest#readme
源码仓库：https://github.com/apollographql/datasource-rest

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：112 kB，21 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=16\.14
- 运行依赖：9 个
- peer 依赖：graphql ^16\.5\.0
- 首次发布：2022-08-19T22:18:02.490Z
- 最新版本发布：2024-12-06T16:15:03.286Z
- 近 30 天下载量：611275（2026-10-07 至 2026-11-05，npm 官方下载统计）
- 已发布版本数：20（近 12 个月 0 个）

## 中文简介

阿波罗服务器 v4 的 REST 数据源

## 安装

```sh
npm install @apollo/datasource-rest
```

也可以使用：`pnpm add @apollo/datasource-rest`、`yarn add @apollo/datasource-rest`、`bun add @apollo/datasource-rest`

## README

# 阿波罗 REST 数据源

此套件导出 a( :[`RESTDataSource`](https://github.com/apollographql/datasource-rest#apollo-rest-data-source)) 用于从 REST API 中取取取数据并通过 阿波罗服务器内的 GraphQL 将其曝光的类别.

RESTData Source 将 DOM 样式的获取 API 的操作包成 `node-fetch` 并增加以下特征:

-   两层的缓存:
    -   内元“ 请求分解” 功能, 默认避免并行发送相同的 GET( 或 HEAD) 请求 .
    -   “ HTTP 缓存” 在( 可能共享) 中提供浏览器风格缓存 `KeyValueCache` 它遵守标准的 HTTP 缓存头.
-   方便的特性,例如能够将一个非序列化对象指定为 JSON 请求正文,以及可以简单指定 URL 搜索参数
-   处理错误

## 文档

查看 [RESTData 源代码的阿波罗服务器文档](https://www.apollographql.com/docs/apollo-server/data/fetching-rest) 更高级别的细节和实例.

## 使用量

要开始,请安装 `@apollo/datasource-rest` 软件包 :

```bash
npm install @apollo/datasource-rest
```

要定义数据源,请扩展 [`RESTDataSource`](https://github.com/apollographql/datasource-rest/tree/main/src/RESTDataSource.ts) 分类并执行解决者所需的数据获取方法。 然后可以通过阿波罗服务器提供数据源 `context` 执行时的对象.

你实施这些方法,可称为在《公约》中包含的便利方法 [`RESTDataSource`](https://github.com/apollographql/datasource-rest/tree/main/src/RESTDataSource.ts) 类以执行 HTTP 请求,同时使其易于建立查询参数,解析 JSON 结果并处理出错.

```javascript
const { RESTDataSource } = require('@apollo/datasource-rest');

class MoviesAPI extends RESTDataSource {
  override baseURL = 'https://movies-api.example.com/';

  async getMovie(id) {
    return this.get(`movies/${encodeURIComponent(id)}`);
  }

  async getMostViewedMovies(limit = 10) {
    const data = await this.get('movies', {
      params: {
        per_page: limit.toString(), // all params entries should be strings
        order_by: 'most_viewed',
      },
    });
    return data.results;
  }
}
```

### API 参考

`RESTDataSource` 被设计为子分类,以便创建一个API,供服务器的其余部分使用。 它的许多方法都受到保护。 其中包括HTTP取取方法(`fetch`, (中文(简体) ), `get`, (中文(简体) ), `put`, (中文(简体) ), `post`, (中文(简体) ), `patch`, (中文(简体) ), `delete`,以及 `head`您的 API 可以调用, 以及其他可以被推翻的方法来自定义行为 .

此 README 列出所有受保护的方法 。 实践上,如果你想用压倒一切的方法定制行为,读取 [源代码](https://github.com/apollographql/datasource-rest/tree/main/src/RESTDataSource.ts) 这是最好的选择.

#### 构造器

这个 `RESTDataSource` 带入一个 `DataSourceConfig` 它允许压倒某些默认行为.

配置覆盖

-   `cache` - 自定义 [`KeyValueCache`](https://www.npmjs.com/package/@apollo/utils.keyvaluecache) 执行
-   `fetch` - 自定义 [`Fetcher`](https://www.npmjs.com/package/@apollo/utils.fetcher) 执行
-   `logger` - 自定义 [`Logger`](https://www.npmjs.com/package/@apollo/utils.logger) 将取代内部所有伐木活动的 `RESTDataSource`

要覆盖 RESTData 源代码,请参见以下示例代码:

```typescript
const dataSource = new (class extends RESTDataSource {})({
  cache: customCache,
  fetch: customFetcher,
  logger: customLogger,
});
```

#### 属性

##### `baseURL`

用于所有 REST 呼叫的可选值 。 如果设定在您的类执行中, 此基础 URL 将作为所有调用的前缀 。 如果不设定,则转至REST呼叫的值正是所使用的值. 另见 `resolveURL`。 。 。 .

```js
class MoviesAPI extends RESTDataSource {
  override baseURL = 'https://movies-api.example.com/';

  // GET
  async getMovie(id) {
    return this.get(
      `movies/${encodeURIComponent(id)}` // path
    );
  }
}
```

`RESTDataSource` 将字符串解释为诸如 `this.get()` 与浏览器解释网页链接的方式完全相同,其地址与 `this.baseURL`。如果 `this.baseURL` 有非空路径组件 :

-   如果字符串传递到诸如 `this.get()` 以斜线开头,然后相对于 _东道主_ ,而不是完整的基础 URL。 也就是说,如果 `this.baseURL` 实值 `https://foo.com/a/b/c/`,则 `this.get('d')` 决定: `https://foo.com/a/b/c/d`不过 `this.get('/d')` 决定: `https://foo.com/d`。 。 。 .
-   如果碱基 URL 有路径元素,而不以斜线结束,那么给定的路径会替换路径中最后一个元素. 也就是说,如果 `baseURL` 实值 `https://foo.com/a/b/c`, (中文(简体) ), `this.get('d')` 决定: `https://foo.com/a/b/d`

在实践中,这意味着你通常应该设置 `this.baseURL` 到您想要访问的所有 URL 的常见前缀 _包括一条后盾_,您应该通过路径 _没有领先的斜线_ 方法,例如: `this.get()`。 。 。 .

如果资源路径开始于一个像 URL 的东西,因为它包含一个冒号,而您希望它在其路径之后被添加到完整的基础 URL(所以您不能通过它 ) `this.get('/foo:bar')`),你可以通过一条从 `./`一样 `this.get('./foo:bar')`。 。 。 .

##### `httpCache`

这是一个在 HTTP 取取中添加 HTTP- header- 敏感缓存的内部对象 。 其精确的API是本套件的内部,可能会在版本之间变化.

#### B. 压倒一切的方法

##### `cacheKeyFor`

默认, `RESTDatasource` 使用该 `cacheKey` 选项,从请求作为缓存密钥,或请求方法和完整请求 URL 在将请求信息保存到 `KeyValueCache`。覆盖此方法以删除查询参数或计算自定义缓存密钥.

例如,您可以用它来使用头字段或HTTP方法作为缓存密钥的一部分. 尽管我们确实验证了信头字段,并且当它们不匹配时不服务于缓存的响应,但新的响应会覆盖不同信头字段的旧的. (对于HTTP方法,这可能是一件积极的事情,因为你可能想要一个 `POST /foo` 请求停止先前缓存 `GET /foo` ) 从被送回

##### `requestDeduplicationPolicyFor`

默认, `RESTDataSource` 全部去复制 **并行** 离开 **`GET` (或 `HEAD`请求** 在一个内存缓存中,与 `KeyValueCache` 用于HTTP响应缓存. 它假设两个 `GET` (或两个) `HEAD`) 并行向同一URL提出的请求可以共享相同的响应. 当请求返回时,其回复会同时发送给每个请求同一URL的呼叫者,然后从缓存中移除.

如果使用相同的缓存密钥( 默认方法 + URL) 提出请求, 但使用 HTTP 方法除外 `GET` 或者说 `HEAD`,在飞行中请求的分解无效:下一个平行 `GET` (或 `HEAD`)请求相同的URL将会提出新的请求.

您可以以多个方式配置此行为 :

-   您可以更改哪些请求是非重复的,哪些不是.
-   看得出来 `RESTDataSource` 对完成之后开始的新请求,而不仅仅是重叠请求,取消重复请求。 (这是记录不良的行为) `RESTDataSource` (在v 5.0.0
-   您可以独立控制“ 引导键” `KeyValueCache` 缓存密钥 .

你这样做,通过压倒 `requestDeduplicationPolicyFor` 在你的课堂上。 此方法采用 URL 和请求, 并返回带有三种形式之一的策略对象 :

-   `{policy: 'deduplicate-during-request-lifetime', deduplicationKey: string}`: 这是默认行为 `GET` 请求。 如果同一分解密钥的请求正在进行中,则分享其结果。 否则,启动一个请求,允许其他请求在运行时针对它复制,并在请求成功返回后忘记它.
-   `{policy: 'deduplicate-until-invalidated', deduplicationKey: string}`: 这是默认行为 `GET` 在v5之前的版本中请求。 如果同一分解密钥的请求正在进行中,则分享其结果。 否则,启动请求并允许其他请求在运行时对它重复复制. 今后所有政策要求 `deduplicate-during-request-lifetime` 或者说 `deduplicate-until-invalidated` 同样的 `deduplicationKey` 将分享同样的结果,直到从政策开始请求 `do-not-deduplicate` 和匹配条目 `invalidateDeduplicationKeys`。 。 。 .
-   `{ policy: 'do-not-deduplicate'; invalidateDeduplicationKeys?: string[] }`: 这是非- 的默认行为`GET` 请求。 总是运行一个实际的HTTP请求,不允许其他请求针对它去重复. 此外,立即取消任何列出的密钥:新要求 `deduplicationKey` 将不会匹配请求缓存中目前存在的任何请求.

此方法的默认实施是:

```ts
protected requestDeduplicationPolicyFor(
  url: URL,
  request: RequestOptions,
): RequestDeduplicationPolicy {
  const method = request.method ?? 'GET';
  // Start with the cache key that is used for the shared header-sensitive
  // cache. Note that its default implementation does not include the HTTP
  // method, so if a subclass overrides this and allows non-GET/HEADs to be
  // de-duplicated it will be important for it to include (at least!) the
  // method in the deduplication key, so we're explicitly adding GET/HEAD here.
  const cacheKey = this.cacheKeyFor(url, request);
  if (['GET', 'HEAD'].includes(method)) {
    return {
      policy: 'deduplicate-during-request-lifetime',
      deduplicationKey: `${method} ${cacheKey}`,
    };
  } else {
    return {
      policy: 'do-not-deduplicate',
      // Always invalidate GETs and HEADs when a different method is seen on the same
      // cache key (ie, URL), as per standard HTTP semantics. (We don't have
      // to invalidate the key with this HTTP method because we never write
      // it.)
      invalidateDeduplicationKeys: [
        this.cacheKeyFor(url, { ...request, method: 'GET' }),
        this.cacheKeyFor(url, { ...request, method: 'HEAD' }),
      ],
    };
  }
```

要完全禁用复制, 只要总是返回 `do-not-duplicate`. (这不影响 HTTP 头敏感缓存 。)

```ts
class MoviesAPI extends RESTDataSource {
  protected override requestDeduplicationPolicyFor() {
    return { policy: 'do-not-deduplicate' } as const;
  }
}
```

##### `willSendRequest`

处理每个请求开始时都会引用这种方法. 这叫与 `path` 财务报告和审定财务报表 `request` 提供给 `fetch`,保证不空 `headers` 财务报告和审定财务报表 `params` 对象。 如果有的话 `Promise` 它将等到承诺完成后才能继续执行请求。 见 [正在截取](https://npmcn.com/package/%40apollo/datasource-rest/#readme-intercepting-fetches) 用于使用示例的章节.

##### `resolveURL`

在某些情况下,您会希望根据环境或其他背景值设置URL,而不是简单地针对 `this.baseURL`。要做到这一点,你可以覆盖 `resolveURL`数字 :

```ts
import type { KeyValueCache } from '@apollo/utils.keyvaluecache';

class PersonalizationAPI extends RESTDataSource {
  override async resolveURL(path: string, _request: AugmentedRequest) {
    if (!this.baseURL) {
      const addresses = await resolveSrv(path.split("/")[1] + ".service.consul");
      this.baseURL = addresses[0];
    }
    return super.resolveURL(path);
  }
}
```

##### `cacheOptionsFor`

允许设置 `CacheOptions` 用于在 `HTTPCache`。这与只请求缓存分开。 您可以用它来设定 TTL 以秒计的值 。 如果你回来了 `{ttl: 0}`,该响应将不会被存储。 如果您返回一个正数,则 `ttl` 返回 2xx 状态代码,然后是响应 _将_ 被缓存, 无论 HTTP 标题 : 确保这是您想要的 ! (目前没有办法说"只有缓存响应应该按照HTTP头来缓存,而是将TTL修改为特定的东西". ). 请注意,如果您不指定 `ttl` 这里,只是 `GET` 请求被缓存 .

您也可以指定 `cacheOptions` 任何呼吁中的“要求” `get()`, (中文(简体) ), `post()`等 (中文(简体) ). 具体注意 `head()` 电话根本没有缓存,所以不会对 `HEAD` 请求。 这可以是对象,例如: `{ttl: 1}`,或返回对象的函数。 若为 `cacheOptions` 提供, `cacheOptionsFor` 不得称号(即, `this.cacheOptionsFor` 是有效的默认值 `cacheOptions`) (中文(简体) ).

这个 `cacheOptions` 函数和 `cacheOptionsFor` 方法可以是协同的.

```javascript
override cacheOptionsFor() {
  return {
    ttl: 1
  }
}
```

##### `didEncounterError`

> 注:在之前版本的RESTData Source( < v5)中,这个钩子预计会抛出它收到的错误(默认执行确实如此). 不再需要这样做;如下文所述,在引用后将立即投出错误 `didEncounterError`。 。 。 .

您可以执行此钩, 以检查( 或修改) 在取取回、 剖析尸体时所抛出的错误( )`parseBody()`),或由 `throwIfResponseIsError()` 钩. 此钩子收到的错误将在此钩子被引用后立即被抛出 .

您也可以在这里完全抛出不同的错误 。 注意,默认情况下,错误是 `GraphQLError`s(来自 `errorFromResponse`) (中文(简体) ).

##### `parseBody`

这种方法用HTTP响应调用,应当将正文读出并分解成适当的格式. 默认情况下,它会检查是否 `Content-Type` 页眉起于 `application/json` 或结束于 `+json` (只是将标题看成字符串而不使用 Content-Type 解析器)并返回 `response.json()` 如果确实如此或 `response.text()` 如果它没有。 如果你想用不同的方式读取尸体,请覆盖它。 这种方法应完全读取响应;如果不读取,可能导致HTTP缓存内存出漏. 如果您超过此选项, 您可能想要覆盖 `cloneParsedBody` 还有.

##### `cloneParsedBody`

这种方法用于克隆一个正体(供请求去重复特性使用,使多位调用者获得不同的回取值,可以单独变异). 狦 `parseBody` 返回基本 JSON 对象以外的值,您可能也想要覆盖此方法。 如果您使用此类的代码与从复制请求共享状态返回的值一致, 您也可以修改此方法以返回其参数而不克隆 .

##### `shouldJSONSerializeBody`

默认情况下,此方法返回 `true` 如果请求机构是:

-   普通对象或数组
-   带有一个 `toJSON` 方法(这不是 `Buffer` 或命名的类实例 `FormData`(中文(简体) )

您可以覆盖这种方法, 以便序列化其他对象, 如自定义类如 JSON .

##### `throwIfResponseIsError`

解析后,此方法检查一个条件(默认情况下,如果 HTTP 状态是 4xx 或 5xxx ),并扔出一个错误 `errorFromResponse` 如果条件得到满足.

##### `errorFromResponse`

基于响应创建错误.

##### `catchCacheWritePromiseErrors`

此类写入背景中共享的 HTTP- header- 敏感缓存( e, 该写入不是 HTTP 抓取的一部分) 。 它通过 `Promise` 与缓存相关的信息写入此方法。 默认情况下,此方法将添加 a `catch` 处理器到 `Promise` 将任何错误写入 `console.error`。如果您信任所有呼叫者,您可以使用它来进行不同的错误处理,或者不进行错误处理 `fetch` 方法和等待 `httpCache.cacheWritePromise`。 。 。 .

##### `trace`

这种方法将单个请求的整个处理包裹起来; 如果 `NODE_ENV` 环境变量等于 `development`,它记录请求方法、URL和持续时间。 您可以以不同的方式覆盖它以提供可观察性 .

### HTTP 方法

这个 `get` 方法 [`RESTDataSource`](https://github.com/apollographql/datasource-rest/tree/main/src/RESTDataSource.ts) 制作一个 HTTP `GET` 请求并返回其解析后的身体。 同样,也有一些内在方法允许 `POST`, (中文(简体) ), `PUT`, (中文(简体) ), `PATCH`, (中文(简体) ), `DELETE`,以及 `HEAD` 请求。 (本节) `head` 方法返回全数 `FetcherResponse` 而不是身体 因为 `HEAD` 答复没有机构。 )

```javascript
class MoviesAPI extends RESTDataSource {
  override baseURL = 'https://movies-api.example.com/';

  // an example making an HTTP POST request
  async postMovie(movie) {
    return this.post(
      `movies`, // path
      { body: movie }, // request body
    );
  }

  // an example making an HTTP PUT request
  async newMovie(movie) {
    return this.put(
      `movies`, // path
      { body: movie }, // request body
    );
  }

  // an example making an HTTP PATCH request
  async updateMovie(movie) {
    return this.patch(
      `movies`, // path
      { body: { id: movie.id, movie } }, // request body
    );
  }

  // an example making an HTTP DELETE request
  async deleteMovie(movie) {
    return this.delete(
      `movies/${encodeURIComponent(movie.id)}`, // path
    );
  }
}
```

HTTP 帮助器的所有功能( )`get`, (中文(简体) ), `put`, (中文(简体) ), `post`, (中文(简体) ), `patch`, (中文(简体) ), `delete`,以及 `head`)接受用于设置 `body`, (中文(简体) ), `headers`, (中文(简体) ), `params`, (中文(简体) ), `cacheKey`,以及 `cacheOptions` (和其他获取 API 选项).

或者,你可以使用 `fetch` 方法。 这种方法的返回值是一个 `DataSourceFetchResult`,包含 `parsedBody`, (中文(简体) ), `response`,以及其他一些关于操作如何与缓存相互作用的元数据字段.

### 拦截获取

数据源允许您截取获取以设置信头,查询参数,或者对发出的请求进行其他修改. 这通常用于授权或适用于所有请求的其他共同关切。 这个 `constructor` 当类像这样被即时化时,可以被推翻以要求额外的上下文信息:

```ts
class PersonalizationAPI extends RESTDataSource {
  private token: string;

  constructor(token: string) {
    super();
    this.token = token;
  }

  willSendRequest(path, request) {
    // set an authorization header
    request.headers['authorization'] = this.token;
    // or set a query parameter
    request.params.set('api_key', this.token);
  }
}
```

如果您正在使用 TypeScript, 您可以使用 `AugmentedRequest` 类型 `willSendRequest` 签名 :

```ts
import { RESTDataSource, AugmentedRequest } from '@apollo/datasource-rest';

class PersonalizationAPI extends RESTDataSource {
  override baseURL = 'https://personalization-api.example.com/';

  private token: string;
  constructor(token: string) {
    super();
    this.token = token;
  }

  override willSendRequest(_path: string, request: AugmentedRequest) {
    request.headers['authorization'] = this.token;
  }
}
```

### 处理答复

> 寻找 `didReceiveResponse`? 吗? 这段也许对你有意思.

在回复之前你可能需要读取或突变反应. 例如,您可能需要为每个请求登录一个特定的标题 。 要做到这一点,你可以推翻公众 `fetch` 这样的方法 :

```ts
  class MyDataSource extends RESTDataSource {
    override async fetch<TResult>(
      path: string,
      incomingRequest: DataSourceRequest = {}
    ) {
      const result = await super.fetch(path, incomingRequest);
      const header = result.response.headers.get('my-custom-header');
      if (header) {
        console.log(`Found header: ${header}`);
      }
      return result;
    }
  }
```

此示例会影响默认 `fetch` 由父母执行`super`) (中文(简体) ). 我们把我们的台阶附在承诺链上,读取标题,并还原最初的结果,即: `super.fetch` 承诺`{ parsedBody, response }`) (中文(简体) ).

### 与阿波罗服务器整合

为了让解析者访问数据源,您创建并返回您 `context` 函数。 (下例使用阿波罗服务器 4 API

```ts
interface MyContext {
  movies: MoviesAPI;
  personalization: PersonalizationAPI;
}

const server = new ApolloServer<MyContext>({
  typeDefs,
  resolvers,
});

// The context function you provide to your integration should handle constructing your data sources on every request.
const url = await startStandaloneServer(server, {
  async context({ req }) { 
    return {
      moviesAPI: new MoviesAPI(),
      personalizationAPI: new PersonalizationAPI(req.headers['authorization']),
    };
  },
});
```

从我们的解析器,我们可以从上下文获取数据源,并返回结果:

```javascript
const resolvers = {
  Query: {
    movie: async (_source, { id }, { moviesAPI }) => {
      return moviesAPI.getMovie(id);
    },
    mostViewedMovies: async (_source, _args, { moviesAPI }) => {
      return moviesAPI.getMostViewedMovies();
    },
    favorites: async (_source, _args, { personalizationAPI }) => {
      return personalizationAPI.getFavorites();
    },
  },
};
```

## 常见问题

### @apollo/datasource-rest 是什么？

阿波罗服务器 v4 的 REST 数据源

### 如何安装 @apollo/datasource-rest？

在项目目录运行 npm install @apollo/datasource-rest，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：graphql（^16\.5\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @apollo/datasource-rest 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @apollo/datasource-rest 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @apollo/datasource-rest 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=16\.14。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @apollo/datasource-rest 可以商用吗？

@apollo/datasource-rest 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @apollo/datasource-rest 的下载量有多少？

根据 npm 官方下载统计，@apollo/datasource-rest 在 2026/10/07 – 2026/11/05 这 30 天内被下载 611,275 次，日均约 20,376 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @apollo/datasource-rest 还在维护吗？

最新版本 6\.4\.1 发布于 2024/12/07（1\.9 年前）；至今共发布 20 个版本，其中过去 12 个月发布了 0 个；首次发布于 2022/08/20。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

