Skip to content

Commit 816a7f1

Browse files
committed
rename lifecycle hooks, pass request instead options
1 parent a0e6e16 commit 816a7f1

File tree

4 files changed

+65
-41
lines changed

4 files changed

+65
-41
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const datasource = new (class MoviesAPI extends HTTPDataSource {
5151
constructor() {
5252
// global client options
5353
super({
54-
request: {
54+
requestOptions: {
5555
timeout: 2000,
5656
http2: true,
5757
headers: {
@@ -62,12 +62,12 @@ const datasource = new (class MoviesAPI extends HTTPDataSource {
6262
this.baseURL = "https://movies-api.example.com";
6363
})
6464

65-
cacheKey() {}
65+
onCacheKeyCalculation() {}
6666

6767
// lifecycle hooks for logging, tracing and request, response manipulation
68-
didEncounterError() {}
69-
async willSendRequest() {}
70-
async didReceiveResponse() {}
68+
async onRequestError() {}
69+
async beforeRequest() {}
70+
async onResponse() {}
7171

7272
async getMovie(id) {
7373
return this.get(`/movies/${id}`, {

src/http-data-source.ts

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import { DataSource, DataSourceConfig } from 'apollo-datasource'
2-
import got, { Agents, HTTPError, NormalizedOptions, OptionsOfJSONResponseBody, Response } from 'got'
2+
import got, {
3+
Agents,
4+
HTTPError,
5+
NormalizedOptions,
6+
OptionsOfJSONResponseBody,
7+
RequestError,
8+
Response,
9+
CacheError,
10+
UploadError,
11+
ReadError,
12+
MaxRedirectsError,
13+
GotReturn as Request,
14+
} from 'got'
315
import QuickLRU from '@alloc/quick-lru'
416
import AbortController from 'abort-controller'
517

@@ -11,14 +23,14 @@ import { KeyValueCache } from 'apollo-server-caching'
1123

1224
const { HttpsAgent } = HttpAgent
1325

14-
export type Request = OptionsOfJSONResponseBody | NormalizedOptions
26+
export type RequestOptions = OptionsOfJSONResponseBody | NormalizedOptions
1527
export interface LRUOptions {
1628
readonly maxAge?: number
1729
readonly maxSize: number
1830
}
1931

2032
export interface HTTPDataSourceOptions {
21-
request?: OptionsOfJSONResponseBody
33+
requestOptions?: RequestOptions
2234
lru?: LRUOptions
2335
}
2436

@@ -97,91 +109,92 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
97109
}
98110

99111
/**
100-
* DidReceiveResponse is executed after a response has been received.
112+
* onResponse is executed after a response has been received.
101113
* You can manipulate the response by returning a different response.
102114
*
103115
* @param response
104116
* @param _request
105117
* @returns
106118
*/
107-
protected async didReceiveResponse<TResult = unknown>(
119+
protected async onResponse<TResult = unknown>(
108120
response: Response<TResult>,
109121
_request: Request,
110122
): Promise<Response<TResult>> {
111123
return response
112124
}
113125

114126
/**
115-
* CacheKey returns the key for the GET request.
127+
* onCacheKeyCalculation returns the key for the GET request.
116128
* The key is used to memoize the request in the LRU cache.
117129
*
118130
* @param request
119131
* @returns
120132
*/
121-
protected cacheKey(request: Request): string {
122-
if (request.url) return request.url.toString()
133+
protected onCacheKeyCalculation(requestOptions: RequestOptions): string {
134+
if (requestOptions.url) return requestOptions.url.toString()
123135
throw new Error('No Cache key provided')
124136
}
125137

126138
/**
127-
* WillSendRequest is executed before a request is made and isn't executed for memoized calls.
139+
* beforeRequest is executed before a request is made and isn't executed for memoized calls.
128140
* You can manipulate the request e.g add/remove headers.
129141
*
130142
* @param request
131143
*/
132-
protected willSendRequest?(request?: Request): Promise<void>
144+
protected beforeRequest?(requestOptions?: RequestOptions): Promise<void>
133145

134146
/**
135-
* DidEncounterError is executed for any request error.
147+
* onRequestError is executed for any request error.
136148
* The raw error is passed. The thrown error might be different.
137149
*
138150
* @param _error
151+
* @param _request
139152
*/
140-
protected didEncounterError(_error: Error) {}
153+
protected onRequestError?(_error: Error, _request?: Request): Promise<void>
141154

142155
protected async get<TResult = unknown>(
143156
url: string,
144-
request?: Request,
157+
requestOptions?: RequestOptions,
145158
): Promise<Response<TResult>> {
146159
return await this.request(url, {
147160
method: 'GET',
148-
...request,
161+
...requestOptions,
149162
})
150163
}
151164

152165
protected async post<TResult = unknown>(
153166
url: string,
154-
request?: Request,
167+
requestOptions?: RequestOptions,
155168
): Promise<Response<TResult>> {
156169
return await this.request(url, {
157170
method: 'POST',
158-
...request,
171+
...requestOptions,
159172
})
160173
}
161174

162175
protected async delete<TResult = unknown>(
163176
url: string,
164-
request?: Request,
177+
requestOptions?: RequestOptions,
165178
): Promise<Response<TResult>> {
166179
return await this.request(url, {
167180
method: 'DELETE',
168-
...request,
181+
...requestOptions,
169182
})
170183
}
171184

172185
protected async put<TResult = unknown>(
173186
url: string,
174-
request?: Request,
187+
requestOptions?: RequestOptions,
175188
): Promise<Response<TResult>> {
176189
return await this.request(url, {
177190
method: 'PUT',
178-
...request,
191+
...requestOptions,
179192
})
180193
}
181194

182195
private async performRequest<TResult>(options: NormalizedOptions) {
183-
if (this.willSendRequest != null) {
184-
await this.willSendRequest(options)
196+
if (this.beforeRequest != null) {
197+
await this.beforeRequest(options)
185198
}
186199

187200
const cancelableRequest = got<TResult>(options as OptionsOfJSONResponseBody)
@@ -194,12 +207,10 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
194207

195208
try {
196209
const response = await cancelableRequest
197-
return this.didReceiveResponse<TResult>(response, options)
210+
return this.onResponse<TResult>(response, cancelableRequest)
198211
} catch (error) {
199212
let error_ = error
200213

201-
this.didEncounterError(error)
202-
203214
if (error instanceof HTTPError) {
204215
if (error.response.statusCode === 401) {
205216
error_ = new AuthenticationError(error.message)
@@ -210,6 +221,18 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
210221
}
211222
}
212223

224+
if (
225+
this.onRequestError &&
226+
(error instanceof RequestError ||
227+
error instanceof HTTPError ||
228+
error instanceof CacheError ||
229+
error instanceof UploadError ||
230+
error instanceof ReadError ||
231+
error instanceof MaxRedirectsError)
232+
) {
233+
await this.onRequestError(error, error.request)
234+
}
235+
213236
throw error_
214237
} finally {
215238
this.abortController.signal.removeEventListener('abort', abort)
@@ -218,7 +241,7 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
218241

219242
private async request<TResult = unknown>(
220243
path: string,
221-
request: Request,
244+
requestOptions: RequestOptions,
222245
): Promise<Response<TResult>> {
223246
const options = got.mergeOptions(
224247
{
@@ -230,12 +253,12 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
230253
prefixUrl: this.baseURL,
231254
},
232255
{
233-
...this.options?.request,
256+
...this.options?.requestOptions,
234257
},
235-
request,
258+
requestOptions,
236259
)
237260

238-
const cacheKey = this.cacheKey(options)
261+
const cacheKey = this.onCacheKeyCalculation(options)
239262

240263
// Memoize get call for the same data source instance
241264
// data sources are scoped to the current request

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { HTTPDataSource, Request } from './http-data-source'
1+
export { HTTPDataSource, RequestOptions } from './http-data-source'
22
export {
33
Response,
44
CacheError,
@@ -10,4 +10,5 @@ export {
1010
TimeoutError,
1111
RequestError,
1212
UnsupportedProtocolError,
13+
GotReturn as Request
1314
} from 'got'

test/rest-data-source.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ApolloError, AuthenticationError, ForbiddenError } from 'apollo-server-
22
import anyTest, { TestInterface } from 'ava'
33
import { uid } from 'uid'
44
import nock from 'nock'
5-
import { CancelError, Request, HTTPDataSource, TimeoutError } from '../src'
5+
import { CancelError, HTTPDataSource, TimeoutError, RequestOptions } from '../src'
66
import { DataSourceConfig } from 'apollo-datasource'
77

88
const test = anyTest as TestInterface<{ path: string }>
@@ -133,7 +133,7 @@ test('Should be able to define a custom cache key for request memoization', asyn
133133
const dataSource = new (class extends HTTPDataSource {
134134
baseURL = baseURL
135135

136-
cacheKey(_request: Request) {
136+
onCacheKeyCalculation(_requestOptions: RequestOptions) {
137137
return 'foo'
138138
}
139139

@@ -165,7 +165,7 @@ test('Should timeout', async (t) => {
165165

166166
constructor() {
167167
super({
168-
request: {
168+
requestOptions: {
169169
timeout: 100,
170170
},
171171
})
@@ -200,7 +200,7 @@ test.cb('Should abort request', (t) => {
200200

201201
constructor() {
202202
super({
203-
request: {
203+
requestOptions: {
204204
timeout: 1000,
205205
},
206206
})
@@ -245,8 +245,8 @@ test('Should be able to modify request in willSendRequest', async (t) => {
245245
const dataSource = new (class extends HTTPDataSource {
246246
baseURL = baseURL
247247

248-
async willSendRequest(request: Request) {
249-
request.headers = {
248+
async beforeRequest(requestOptions: RequestOptions) {
249+
requestOptions.headers = {
250250
'X-Foo': 'bar',
251251
}
252252
}

0 commit comments

Comments
 (0)