Skip to content

Commit

Permalink
graphql: Add a GraphQLError error class
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Sep 15, 2024
1 parent b00b691 commit c4e992a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-windows-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solid-primitives/graphql": minor
---

Add a `GraphQLError` error class, and throw it in case of a request error, instead of thowing an errors array
58 changes: 36 additions & 22 deletions packages/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ export type GraphQLResourceSource<V extends object = {}> = {
options: RequestOptions<V>;
};

/**
A GraphQLError describes an Error found during the parse, validate,
or execute phases of performing a GraphQL operation.
*/
export class GraphQLError extends Error {
constructor(
message: string,
public locations?: { line: number; column: number }[],
public extensions?: Record<string, any>
) {
super(message);
}
}

/**
* A function returned by {@link createGraphQLClient}.
* It wraps {@link createResource} and performs a GraphQL fetch to endpoint form the client.
Expand Down Expand Up @@ -96,23 +110,23 @@ export async function request<T = any, V extends object = {}>(
query: string | DocumentNode | TypedDocumentNode<T, V>,
options: RequestOptions<V> = {},
): Promise<T> {
const { fetcher = fetch, variables = {}, headers = {}, method = "POST" } = options;
const query_ = typeof query == "string" ? query : print(query);
const { fetcher = fetch, variables = {}, method = "POST" } = options;
const query_string = typeof query == "string" ? query : print(query);

return fetcher(url, {
const res = await fetcher(url, {
...options,
method,
body: JSON.stringify({ query: query_, variables }),
body: JSON.stringify({ query: query_string, variables }),
headers: {
"content-type": "application/json",
...headers,
"content-type": "application/json",
...options.headers,
},
})
.then((r: any) => r.json())
.then(({ data, errors }: any) => {
if (errors) throw errors;
return data;
});
const data = await res.json()
if (data.errors && data.errors.length) {
throw new GraphQLError(data.errors[0].message, data.errors[0].locations, data.errors[0].extensions)
}
return data.data
}

/**
Expand All @@ -130,23 +144,23 @@ export async function multipartRequest<T = any, V extends object = {}>(
query: string | DocumentNode | TypedDocumentNode<T, V>,
options: Omit<RequestOptions<V>, "method"> = {},
): Promise<T> {
const { fetcher = fetch, variables = {}, headers = {} } = options;
const query_ = typeof query == "string" ? query : print(query);
const { fetcher = fetch, variables = {} } = options;
const query_string = typeof query == "string" ? query : print(query);

return fetcher(url, {
const res = await fetcher(url, {
...options,
method: "POST",
body: makeMultipartBody(query_, variables),
body: makeMultipartBody(query_string, variables),
headers: {
"content-type": "multipart/form-data",
...headers,
...options.headers,
},
})
.then((r: any) => r.json())
.then(({ data, errors }: any) => {
if (errors) throw errors;
return data;
});
const data = await res.json()
if (data.errors && data.errors.length) {
throw new GraphQLError(data.errors[0].message, data.errors[0].locations, data.errors[0].extensions)
}
return data.data
}

/**
Expand All @@ -156,7 +170,7 @@ export async function multipartRequest<T = any, V extends object = {}>(
* @param variables variables used in the mutation (File and Blob instances can be used as values).
* @returns a FormData object, ready to be POSTed
*/
export function makeMultipartBody(query: string, variables: object) {
export function makeMultipartBody(query: string, variables: object): FormData {
const parts: { blob: Blob; path: string }[] = [];

// We don't want to modify the variables passed in as arguments
Expand Down

0 comments on commit c4e992a

Please sign in to comment.