Skip to content

GQty GraphQL Error Handling for Pylon and maybe Hono #2057

Description

@kleberbaum

Hi everyone,

Goal: Establish sensible defaults for GQty error handling in generated client for Pylon.

In my previous issue, I provided a general outline of how GQty could function on the server side. This issue is specifically addressing error handling in a Pylon backend services using GQty:

4. Error Handling with resolve

  • Unclear Documentation
    GQty’s documentation doesn’t clearly demonstrate how to handle GraphQL errors when using resolve on the server. If there are recommended patterns or best practices, I’d love to learn about them ❤

Originally posted by [@kleberbaum](https://github.com/kleberbaum) in [#2051 (issue)](#2051)

With some assistance from the Discord community, I implemented a general error handling strategy for my web service by modifying the generated client/index.ts as shown below. Since I’m utilizing GQty’s resolve(), I expect to receive only one GraphQL error per request, so I handle it using error.graphQLErrors[0]:

/**
 * GQty: You can safely modify this file based on your needs.
 */

import {
  Cache,
  createClient,
  defaultResponseHandler,
  GQtyError,
  type QueryFetcher,
} from 'gqty';
import {
  generatedSchema,
  scalarsEnumsHash,
  type GeneratedSchema,
} from './schema.generated';
import { GraphQLError } from 'graphql';

const queryFetcher: QueryFetcher = async function (
  { query, variables, operationName, extensions },
  fetchOptions
) {
  let response;
  let data;

  try {
    // Modify the URL "https://iam.netsnek.workers.dev/graphql" if necessary
    const response = await fetch('https://iam.netsnek.workers.dev/graphql', {
      method: 'POST',
      headers: {
        'Authorization': extensions?.authToken as string,	
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query,
        variables,
        operationName,
      }),
      ...fetchOptions,
    });

    data = await defaultResponseHandler(response);
  }
  catch (error) {
    if (error instanceof GQtyError && error.graphQLErrors?.[0]) {
      throw new GraphQLError(error.graphQLErrors[0].message, {
        extensions: error.graphQLErrors[0].extensions,
        //path: error.graphQLErrors[0].path,
      });
    }
  }

  return data;
};

const cache = new Cache(
  undefined,
  /**
   * Default option is immediate cache expiry but retain data for 5 minutes,
   * allowing soft refetches in the background.
   */
  {
    maxAge: 0,
    staleWhileRevalidate: 5 * 60 * 1000,
    normalization: true,
  }
);

export const client = createClient<GeneratedSchema>({
  schema: generatedSchema,
  scalars: scalarsEnumsHash,
  cache,
  fetchOptions: {
    fetcher: queryFetcher,
  },
});

// Core functions
export const { resolve, subscribe, schema } = client;

// Legacy functions
export const {
  query,
  mutation,
  mutate,
  subscription,
  resolved,
  refetch,
  track,
} = client;

export * from './schema.generated';

When a GQty error is caught, I extract the GraphQL error and rethrow it to ensure the same error appears in the Pylon playground. I omit the path because it should reflect the Pylon path, but all other error details are preserved from the original GQty error. The result is shown in the image below:

Error Screenshot

I’m looking forward to any feedback or suggestions to further refine this error handling approach @vicary @schettn.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions