Skip to content

Commit

Permalink
fix: improve typings for node-fetch (#56)
Browse files Browse the repository at this point in the history
* fix: improve typings for node-fetch

* fix
  • Loading branch information
lynxtaa authored Jan 20, 2021
1 parent 82ae691 commit 62a02ab
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 25 deletions.
33 changes: 18 additions & 15 deletions src/AwesomeGraphQLClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,28 @@ import assert from './util/assert'
import formatGetRequestUrl from './util/formatGetRequestUrl'
import isExtractableFileEnhanced from './util/isExtractableFileEnhanced'
import isResponseJSON from './util/isResponseJSON'
import { RequestResult } from './util/types'

export default class AwesomeGraphQLClient<
TQuery = string,
FetchOptions extends { [key: string]: any } = RequestInit
TFetchOptions extends { [key: string]: any } = RequestInit,
TRequestResult extends RequestResult = Response
> {
private endpoint: string
private fetch: (url: string, options?: FetchOptions) => Promise<any>
private fetchOptions?: FetchOptions
private fetch: (url: string, options?: TFetchOptions) => Promise<TRequestResult>
private fetchOptions?: TFetchOptions
private formatQuery?: (query: TQuery) => string
private FormData: any

constructor(config: {
/** GraphQL endpoint */
endpoint: string
/** Fetch polyfill if necessary */
fetch?: (url: string, options?: any) => Promise<any>
fetch?: (url: string, options?: any) => Promise<TRequestResult>
/** FormData polyfill if necessary */
FormData?: any
/** Overrides for fetch options */
fetchOptions?: FetchOptions
fetchOptions?: TFetchOptions
/** Custom query formatter */
formatQuery?: (query: TQuery) => string
}) {
Expand All @@ -39,7 +41,7 @@ export default class AwesomeGraphQLClient<
)

this.endpoint = config.endpoint
this.fetch = config.fetch || fetch.bind(null)
this.fetch = config.fetch || (fetch.bind(null) as any)
this.fetchOptions = config.fetchOptions

this.FormData =
Expand Down Expand Up @@ -89,14 +91,14 @@ export default class AwesomeGraphQLClient<
*
* @param fetchOptions new overrides for fetch options
*/
setFetchOptions(fetchOptions: FetchOptions): void {
setFetchOptions(fetchOptions: TFetchOptions): void {
this.fetchOptions = fetchOptions
}

/**
* Returns current overrides for fetch options
*/
getFetchOptions(): FetchOptions | undefined {
getFetchOptions(): TFetchOptions | undefined {
return this.fetchOptions
}

Expand All @@ -119,9 +121,10 @@ export default class AwesomeGraphQLClient<
async requestSafe<TData extends {}, TVariables extends {} = {}>(
query: TQuery,
variables?: TVariables,
fetchOptions?: FetchOptions,
fetchOptions?: TFetchOptions,
): Promise<
{ data: TData; response: Response } | { error: GraphQLRequestError | Error }
| { data: TData; response: TRequestResult }
| { error: GraphQLRequestError<TRequestResult> | Error }
> {
try {
const queryAsString = this.formatQuery ? this.formatQuery(query) : query
Expand All @@ -141,15 +144,15 @@ export default class AwesomeGraphQLClient<
},
}

let response: Response
let response: TRequestResult | Response

if (options.method.toUpperCase() === 'GET') {
const url = formatGetRequestUrl({
endpoint: this.endpoint,
query: queryAsString,
variables,
})
response = await this.fetch(url, (options as unknown) as FetchOptions)
response = await this.fetch(url, (options as unknown) as TFetchOptions)
} else {
const body = this.createRequestBody(queryAsString, variables)

Expand All @@ -160,11 +163,11 @@ export default class AwesomeGraphQLClient<
typeof body === 'string'
? { ...options.headers, 'Content-Type': 'application/json' }
: options.headers,
} as unknown) as FetchOptions)
} as unknown) as TFetchOptions)
}

if (!response.ok) {
if (isResponseJSON(response)) {
if (isResponseJSON(response as any)) {
const { errors } = await response.json()

if (errors?.[0]?.message) {
Expand Down Expand Up @@ -215,7 +218,7 @@ export default class AwesomeGraphQLClient<
async request<TData extends {}, TVariables extends {} = {}>(
query: TQuery,
variables?: TVariables,
fetchOptions?: FetchOptions,
fetchOptions?: TFetchOptions,
): Promise<TData> {
const result = await this.requestSafe<TData, TVariables>(
query,
Expand Down
25 changes: 16 additions & 9 deletions src/GraphQLRequestError.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
interface GraphQLRequestErrorOptions {
query: string
variables?: Record<string, unknown>
response: Response
message: string
}
import { RequestResult } from './util/types'

export default class GraphQLRequestError extends Error {
export default class GraphQLRequestError<
TResponse extends RequestResult = Response
> extends Error {
query: string
variables?: Record<string, unknown>
response: Response
response: TResponse

constructor({ query, variables, response, message }: GraphQLRequestErrorOptions) {
constructor({
query,
variables,
response,
message,
}: {
query: string
variables?: Record<string, unknown>
response: TResponse
message: string
}) {
super(`GraphQL Request Error: ${message}`)

this.query = query
Expand Down
4 changes: 3 additions & 1 deletion src/util/isResponseJSON.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const isResponseJSON = (response: Response): boolean =>
import { RequestResult } from './types'

const isResponseJSON = (response: { headers: RequestResult['headers'] }): boolean =>
(response.headers.get('Content-Type') || '').includes('application/json')

export default isResponseJSON
10 changes: 10 additions & 0 deletions src/util/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface Headers {
get(name: string): string | null
}

export interface RequestResult {
ok: boolean
headers: Headers
json: () => Promise<any>
status: number
}

0 comments on commit 62a02ab

Please sign in to comment.