Search code examples
reactjsgraphqlredux-thunkredux-toolkitrtk-query

How to handle error format in Redux-toolkit rtk-query graphql application


I'm developing an application based on redux-toolkit rtk-query and graphql. I use graphql-codegen to generate the reducers starting from the graphql schema and everything working as expected.

Now i have a problem to handle errors. Has i understand redux-toolkit raise custom error with a specific format like this

{
  name: "Error",
  message: "System error",
  stack:
    'Error: System error: {"response":{"errors":[{"message":"System error","locations":[{"line":3,"column":3}],"path":["completaAttivita"],"extensions":{"errorCode":505,"classification":"VALIDATION","errorMessage":"Messaggio di errore","verboseErrorMessage":"it.cmrc.sid.backend.exception.CustomException: I riferimenti contabili non sono più validi","causedBy":"No Cause!"}}],"data":{"completaAttivita":null},"status":200,"headers":{"map":{"content-length":"398","content-type":"application/json"}}},"request":{"query":"\\n    mutation completaAttivita($taskName: TipoAttivita, $taskId: String, $determinaId: BigInteger, $revisione: Boolean, $nota: NotaInputInput, $avanzaStatoDetermina: Boolean, $attribuzioniOrizzontali: AttribuzioniOrizzontaliInputInput, $firmaInput: FirmaInputInput, $roles: [String]) {\\n  completaAttivita(\\n    taskName: $taskName\\n    taskId: $taskId\\n    determinaId: $determinaId\\n    revisione: $revisione\\n    nota: $nota\\n    avanzaStatoDetermina: $avanzaStatoDetermina\\n    attribuzioniOrizzontali: $attribuzioniOrizzontali\\n    firmaInput: $firmaInput\\n    roles: $roles\\n  ) {\\n    id\\n  }\\n}\\n    ","variables":{"taskId":"24ac495b-46ca-42f4-9be2-fd92f0398114","determinaId":1342,"taskName":"firmaDirigente","firmaInput":{"username":"fdfs","password":"fdsf","otp":"fdsdf"}}}}\n    at eval (webpack-internal:///../../node_modules/graphql-request/dist/index.js:354:31)\n    at step (webpack-internal:///../../node_modules/graphql-request/dist/index.js:63:23)\n    at Object.eval [as next] (webpack-internal:///../../node_modules/graphql-request/dist/index.js:44:53)\n    at fulfilled (webpack-internal:///../../node_modules/graphql-request/dist/index.js:35:58)'
};

But my graphql endpoint return this

{
  errors: [
    {
      message: "System error",
      locations: [{ line: 3, column: 3 }],
      path: ["completaAttivita"],
      extensions: {
        errorCode: 505,
        classification: "VALIDATION",
        errorMessage: "Messaggio di errore",
        verboseErrorMessage:
          "it.cmrc.sid.backend.exception.CustomException: Messaggio di errore",
        causedBy: "No Cause!"
      }
    }
  ],
  data: { completaAttivita: null }
};

Using rtk-query and the autogenerated client i have no access to the complete response from server. And i need to extract the error messagge in the exceptions object.

From redix-toolkit documentation i understand that i need to catch the error and call rejectwithvalue() from a createAsyncThunk but i dont'undertand of to do that.

Here the base api object

import { createApi } from '@reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from './base-request';
import { GraphQLClient } from 'graphql-request';
import { getSession } from 'next-auth/react';

export const client = new GraphQLClient(
  `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
  {
    credentials: 'same-origin',
    headers: {
      Accept: 'application/json'
    }
  }
);

export const api = createApi({
  baseQuery: graphqlRequestBaseQuery({
    client,
    prepareHeaders: async (headers, { getState }) => {
      const session = await getSession();
      if (session) {
        headers.set('Authorization', `Bearer ${session?.access_token}`);
      }

      return headers;
    }
  }),
  endpoints: () => ({}),
  refetchOnMountOrArgChange: true
});


Solution

  • Thanks to @phry for merge my solution.

    @rtk-query/graphql-request-base-query (version > 2.1.0) introduce a new configuration to handle errors format. Here a small explanation.

    Typization

    
    graphqlRequestBaseQuery<CustomErrorFormat>
    
    

    Custom Error handler

    ...
       customErrors: (props: ClientError) => CustomErrorFormat
    ...
    

    Full example https://codesandbox.io/s/headless-microservice-uzujqb?file=/src/App.tsx

    import { createApi } from '@reduxjs/toolkit/query/react';
    import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
    import { ClientError, GraphQLClient } from 'graphql-request';
    import { getSession } from 'next-auth/react';
    
    export const client = new GraphQLClient(
      `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
      {
        credentials: 'same-origin',
        headers: {
          Accept: 'application/json'
        }
      }
    );
    
    export const api = createApi({
      baseQuery: graphqlRequestBaseQuery<
        Partial<ClientError & { errorCode: number }>
      >({
        client,
        prepareHeaders: async (headers, { getState }) => {
          const session = await getSession();
          if (session) {
            headers.set('Authorization', `Bearer ${session?.access_token}`);
          }
    
          return headers;
        },
        customErrors: ({ name, stack, response }) => {
          const { errorMessage = '', errorCode = 500 } = response?.errors?.length
            ? response?.errors[0]?.extensions
            : {};
    
          return {
            name,
            message: errorMessage,
            errorCode,
            stack
          };
        }
      }),
      endpoints: () => ({}),
      refetchOnMountOrArgChange: true
    });