Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/red-trains-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@apollo/datasource-rest': minor
---

Add support for responseType option
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ You can also throw a different error here altogether. Note that by default, erro

This method is called with the HTTP response and should read the body and parse it into an appropriate format. By default, it checks to see if the `Content-Type` header starts with `application/json` or ends with `+json` (just looking at the header as a string without using a Content-Type parser) and returns `response.json()` if it does or `response.text()` if it does not. If you want to read the body in a different way, override this. This method should read the response fully; if it does not, it could cause a memory leak inside the HTTP cache. If you override this, you may want to override `cloneParsedBody` as well.

You can send option to within the request called `responseType`, which will override the logic above and return the
response with given type. Supported types are `arraybuffer`, `text` and `json`. The default value to use is `json`.

##### `cloneParsedBody`

This method is used to clone a body (for use by the request deduplication feature so that multiple callers get distinct return values that can be separately mutated). If your `parseBody` returns values other than basic JSON objects, you might want to override this method too. You can also change this method to return its argument without cloning if your code that uses this class is OK with the values returned from deduplicated requests sharing state.
Expand Down Expand Up @@ -294,6 +297,26 @@ All of the HTTP helper functions (`get`, `put`, `post`, `patch`, `delete`, and `

Alternatively, you can use the `fetch` method. The return value of this method is a `DataSourceFetchResult`, which contains `parsedBody`, `response`, and some other fields with metadata about how the operation interacted with the cache.

### Setting response type option

If you would like to hard set the response type fetch should treat the source response with, you can use
`responseType` option to set it.

Example of getting the response as `arraybuffer`. Supported values are `text`, `arraybuffer` and `json`.

```ts
class PersonalizationAPI extends RESTDataSource {
// Get ArrayBuffer
async getMoviePosterPDF(id) {
return this.get(
`movies/posters/${encodeURIComponent(id)}`, {
responseType: 'arraybuffer',
}
);
}
}
```

### Intercepting fetches

Data sources allow you to intercept fetches to set headers, query parameters, or make other changes to the outgoing request. This is most often used for authorization or other common concerns that apply to all requests. The `constructor` can be overridden to require additional contextual information when the class is instantiated like so:
Expand Down
1 change: 1 addition & 0 deletions cspell-dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ singletonizes
unmock
userland
withrequired
arraybuffer
31 changes: 29 additions & 2 deletions src/RESTDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ import isPlainObject from 'lodash.isplainobject';
import { HTTPCache } from './HTTPCache';

export type ValueOrPromise<T> = T | Promise<T>;
export type RequestResponseType = 'arraybuffer' | 'text' | 'json';

export type RequestOptions<CO extends CacheOptions = CacheOptions> =
FetcherRequestInit & {
/**
* `responseType` indicates the type of data that the server will respond with
* options are: 'arraybuffer', 'text', 'json'
* Defaults to JSON.
*/
responseType?: RequestResponseType;
/**
* URL search parameters can be provided either as a record object (in which
* case keys with `undefined` values are ignored) or as an URLSearchParams
Expand Down Expand Up @@ -299,9 +306,26 @@ export abstract class RESTDataSource<CO extends CacheOptions = CacheOptions> {
//
// If you override this to return interesting new mutable data types, override
// cloneParsedBody too.
protected parseBody(response: FetcherResponse): Promise<object | string> {
protected parseBody(
response: FetcherResponse,
requestResponseTypeOption: RequestResponseType | undefined,
): Promise<object | string> {
const contentType = response.headers.get('Content-Type');
const contentLength = response.headers.get('Content-Length');

if (requestResponseTypeOption) {
switch (requestResponseTypeOption) {
case 'arraybuffer':
return response.arrayBuffer();
case 'text':
return response.text();
case 'json':
return response.json();
default:
return response.json();
}
}

if (
// As one might expect, a "204 No Content" is empty! This means there
// isn't enough to `JSON.parse`, and trying will result in an error.
Expand Down Expand Up @@ -551,7 +575,10 @@ export abstract class RESTDataSource<CO extends CacheOptions = CacheOptions> {
this.catchCacheWritePromiseErrors(cacheWritePromise);
}

const parsedBody = await this.parseBody(response);
const parsedBody = await this.parseBody(
response,
outgoingRequest.responseType,
);

await this.throwIfResponseIsError({
url,
Expand Down