graphql-request
graphql-request copied to clipboard
Typing the error response
Hi,
I was trying to type the errors received from the standard request function, and digging around the code I saw a ClientError interface that perhaps I could use.
Consider the following code:
import { ClientError, request } from 'graphql-request';
try {
const res = await request(url, query, variables);
} catch (error) {
const clientError = error as ClientError;
const graphqlErrors = clientError.response.errors; // typed as GraphQLError[] | undefined
}
Problems
I found using the above approach is problematic for two reasons.
No extensions property
According to the June 2018 GraphQL spec:
GraphQL services may provide an additional entry to errors with key extensions. This entry, if set, must have a map as its value. This entry is reserved for implementors to add additional information to errors however they see fit, and there are no additional restrictions on its contents.
However, the errors property within clientError.response does not include the extensions property.
https://github.com/prisma-labs/graphql-request/blob/a8d99f5cdbe57786ecb8d99c88175599608d2fc6/src/types.ts#L5-L9
The graphql package includes a GraphQLFormattedError interface, but nothing similar to ClientError from graphql-request. I guess one option would be to extend it, but it seems like a somewhat fragile approach.
Not always a GraphQLResponse
The caught error is not necessarily a GraphQLReponse (e.g. 404). This is where the error codes from the server make sense, I suppose.
Is there a better way to type the response error, other than extending ClientError?
@davelsan any specific reason this was closed? I think the issue with the extensions type not being included on the error object is still a problem.
Hi @hermanator608, thanks for contributing to the issue. I apologize for the late reply. The reason I closed it is that it was posted more as a usage question than an actual report. I am re-opening it.
In that project, I (somewhat clumsily) solved this problem by extending the ClientError interface with a custom request field, which in turn included an errors field of type GraphQLFormattedError<TExtensions>.
It was similar to the snippet below, though we used a more specific solution for our known server errors. I think a generic workaround could have been something like this:
import { GraphQLFormattedError } from 'graphql';
import { ClientError } from 'graphql-request';
import { GraphQLResponse } from 'graphql-request/dist/types';
interface ExtendedGraphQLResponse<
TData = unknown,
TExtensions = Record<string, unknown>
> extends Pick<GraphQLResponse<TData>, 'data' | 'extensions' | 'status'> {
errors?: GraphQLFormattedError<TExtensions>[];
// [key: string]: unknown;
}
export interface ExtendedClientError<
TData = unknown,
TExtensions extends Record<string, unknown> = Record<string, unknown>
> extends Omit<ClientError, 'response'> {
response: ExtendedGraphQLResponse<TData, TExtensions>;
}
Then it could be used like so:
type DataType = {
dataField1: string;
dataField2: string;
};
type ExtensionsType = {
extField1: string;
extField2: string;
};
const clientError = /* get the error from graphql-request */ as ExtendedClientError<DataType, ExtensionsType>;
clientError.response.data; // typeof DataType | undefined
clientError.response.errors?.[0].extensions; // typeof ExtensionsType | undefined
But like I said in the original issue, this is a fragile approach. Perhaps a more robust solution would be to use the GraphQLFormattedError type from graphql in the GraphQLResponse interface.
import { GraphQLFormattedError } from 'graphql';
export interface GraphQLResponse<
TData = any,
TErrorExtensions extends Record<string, any> = Record<string, any>
> {
data?: TData;
errors?: GraphQLFormattedError<TErrorExtensions>[];
extensions?: any;
status: number;
[key: string]: any;
}
Any news on this issue?
I have an overhauled error system design proposal here https://github.com/jasonkuhrt/graphql-request/issues/509.