Skip to content

Commit 3252179

Browse files
committed
fix(error-handling): Better error handling
1 parent ace509a commit 3252179

File tree

8 files changed

+87
-75
lines changed

8 files changed

+87
-75
lines changed

package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@
33
"version": "0.1.0",
44
"main": "dist/src/index.js",
55
"typings": "./dist/src/index.d.ts",
6-
"files": ["dist"],
6+
"files": [
7+
"dist"
8+
],
79
"repository": {
810
"type": "git",
911
"url": "git+https://github.com/graphcool/http-link-dataloader.git"
1012
},
11-
"keywords": ["graphql", "request", "fetch", "graphql-client", "apollo"],
13+
"keywords": [
14+
"graphql",
15+
"request",
16+
"fetch",
17+
"graphql-client",
18+
"apollo"
19+
],
1220
"author": "Tim Suchanek <tim.suchanek@gmail.com>",
1321
"license": "MIT",
1422
"bugs": {
@@ -27,6 +35,7 @@
2735
"ava": "^0.25.0",
2836
"fetch-mock": "^6.0.0",
2937
"tslint": "^5.9.1",
38+
"tslint-config-prettier": "^1.7.0",
3039
"tslint-config-standard": "^7.0.0",
3140
"typescript": "^2.7.1"
3241
},

src/BatchedGraphQLClient.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { ClientError, Options, Variables } from './types'
1+
import {
2+
Options,
3+
Variables,
4+
} from './types'
25
import 'cross-fetch/polyfill'
36
import * as DataLoader from 'dataloader'
4-
5-
export { ClientError } from './types'
7+
import { ClientError } from './ClientError'
68

79
export class BatchedGraphQLClient {
810
public uri: string
@@ -42,32 +44,33 @@ export class BatchedGraphQLClient {
4244
body,
4345
})
4446

45-
const results = await getResults(response)!
47+
const results = await getResults(response)
4648

47-
const allResultsHaveData =
48-
results.filter(r => r.data).length === results.length
49+
if (Array.isArray(results)) {
50+
const allResultsHaveData =
51+
results.filter(r => r.data).length === results.length
4952

50-
if (response.ok && !results.find(r => r.errors) && allResultsHaveData) {
51-
return results.map(r => r.data)
53+
if (response.ok && !results.find(r => r.errors) && allResultsHaveData) {
54+
return results.map(r => r.data)
55+
} else {
56+
const errorIndex = results.findIndex(r => r.errors)
57+
const result = results[errorIndex]
58+
const errorResult =
59+
typeof result === 'string' ? { error: result } : result
60+
throw new ClientError({ ...errorResult, status: response.status })
61+
}
5262
} else {
53-
const errorIndex = results.findIndex(r => r.errors)
54-
const result = results[errorIndex]
55-
const { query, variables } = requests[errorIndex]
56-
const errorResult =
57-
typeof result === 'string' ? { error: result } : result
58-
throw new ClientError(
59-
{ ...errorResult, status: response.status },
60-
{ query, variables },
61-
)
63+
// if it is not an array, there must be an error
64+
throw new ClientError({ ...results, status: response.status })
6265
}
6366
}
6467
}
6568

66-
async function getResults(response: Response): Promise<any> {
69+
function getResults(response: Response): Promise<any> {
6770
const contentType = response.headers.get('Content-Type')
6871
if (contentType && contentType.startsWith('application/json')) {
69-
return await response.json()
72+
return response.json()
7073
} else {
71-
return await response.text()
74+
return response.text()
7275
}
7376
}

src/ClientError.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { GraphQLResponse, GraphQLRequestContext } from './types'
2+
3+
export class ClientError extends Error {
4+
result: GraphQLResponse
5+
request: GraphQLRequestContext
6+
7+
constructor(result: GraphQLResponse) {
8+
const message = ClientError.extractMessage(result)
9+
10+
super(message)
11+
12+
this.result = result
13+
14+
// this is needed as Safari doesn't support .captureStackTrace
15+
/* tslint:disable-next-line */
16+
if (typeof Error.captureStackTrace === 'function') {
17+
Error.captureStackTrace(this, ClientError)
18+
}
19+
}
20+
21+
private static extractMessage(response: GraphQLResponse): string {
22+
try {
23+
return response.errors![0].message
24+
} catch (e) {
25+
return `GraphQL Error (Code: ${response.status})`
26+
}
27+
}
28+
}

src/HTTPLinkDataloader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ApolloLink, Observable, Operation } from 'apollo-link'
22
import { print } from 'graphql'
33
import { BatchedGraphQLClient } from './index'
4-
import { Options, HttpOptions } from './types'
4+
import { HttpOptions } from './types'
55

66
export class HTTPLinkDataloader extends ApolloLink {
77
constructor(options: HttpOptions) {

src/types.ts

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface Options {
1313
}
1414

1515
export type HttpOptions = Options & {
16-
uri: string
16+
uri: string,
1717
}
1818

1919
export interface GraphQLError {
@@ -33,34 +33,3 @@ export interface GraphQLRequestContext {
3333
query: string
3434
variables?: Variables
3535
}
36-
37-
export class ClientError extends Error {
38-
response: GraphQLResponse
39-
request: GraphQLRequestContext
40-
41-
constructor(response: GraphQLResponse, request: GraphQLRequestContext) {
42-
const message = `${ClientError.extractMessage(response)}: ${JSON.stringify({
43-
response,
44-
request,
45-
})}`
46-
47-
super(message)
48-
49-
this.response = response
50-
this.request = request
51-
52-
// this is needed as Safari doesn't support .captureStackTrace
53-
/* tslint:disable-next-line */
54-
if (typeof Error.captureStackTrace === 'function') {
55-
Error.captureStackTrace(this, ClientError)
56-
}
57-
}
58-
59-
private static extractMessage(response: GraphQLResponse): string {
60-
try {
61-
return response.errors![0].message
62-
} catch (e) {
63-
return `GraphQL Error (Code: ${response.status})`
64-
}
65-
}
66-
}

tests/index.test.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import test from 'ava'
22
import * as fetchMock from 'fetch-mock'
3-
import { BatchedGraphQLClient, ClientError } from '../src/index'
3+
import { BatchedGraphQLClient } from '../src/index'
44
import { Options } from '../src/types'
55

6-
test('extra fetch options', async (t) => {
6+
test('extra fetch options', async t => {
77
const options: Options = {
88
credentials: 'include',
99
mode: 'cors',
1010
cache: 'reload',
1111
}
1212

13-
const client = new BatchedGraphQLClient('https://mock-api.com/graphql', options)
14-
await mock({
15-
body: [{ data: {test: 'test'} }]
16-
}, async () => {
17-
await client.request('{ test }')
18-
const actualOptions = fetchMock.lastCall()[1]
19-
for (let name in options) {
20-
t.deepEqual(actualOptions[name], options[name])
21-
}
22-
})
13+
const client = new BatchedGraphQLClient(
14+
'https://mock-api.com/graphql',
15+
options,
16+
)
17+
await mock(
18+
{
19+
body: [{ data: { test: 'test' } }],
20+
},
21+
async () => {
22+
await client.request('{ test }')
23+
const actualOptions = fetchMock.lastCall()[1]
24+
for (let name in options) {
25+
t.deepEqual(actualOptions[name], options[name])
26+
}
27+
},
28+
)
2329
})
2430

2531
async function mock(response: any, testFn: () => Promise<void>) {

tslint.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
{
2-
"extends": ["tslint-config-standard"],
2+
"extends": ["tslint-config-standard", "tslint-config-prettier"],
33
"rules": {
4-
"trailing-comma": [
5-
true,
6-
{
7-
"multiline": "always",
8-
"singleline": "never"
9-
}
10-
],
114
"no-use-before-declare": false
125
}
136
}

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,10 @@ tslib@^1.8.0, tslib@^1.8.1:
28382838
version "1.9.0"
28392839
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
28402840

2841+
tslint-config-prettier@^1.7.0:
2842+
version "1.7.0"
2843+
resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.7.0.tgz#1588f794c6863ba31420e8b1d14ae91326063815"
2844+
28412845
tslint-config-standard@^7.0.0:
28422846
version "7.0.0"
28432847
resolved "https://registry.yarnpkg.com/tslint-config-standard/-/tslint-config-standard-7.0.0.tgz#47bbf25578ed2212456f892d51e1abe884a29f15"

0 commit comments

Comments
 (0)