dna
dna

Reputation: 2317

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
});

Upvotes: 4

Views: 4804

Answers (2)

dna
dna

Reputation: 2317

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
});

Upvotes: 4

phry
phry

Reputation: 44226

You can always write a wrapper around your baseQuery to reformat it:

const originalBaseQuery = graphqlRequestBaseQuery(...)

const wrappedBaseQuery = async (...args) => {
  const result = await originalBaseQuery(...args);
  if (result.error) {
    // modify `result.error` here however you want
  }
  return result
}

It could also be necessary that you need to try..catch for that:

const originalBaseQuery = graphqlRequestBaseQuery(...)

const wrappedBaseQuery = async (...args) => {
  try {
    return await originalBaseQuery(...args);
  } catch (e) {
    // modify your error here
     return { error: e.foo.bar }
  }
}

I think this just slipped by when I was writing graphqlRequestBaseQuery and so far nobody has asked about it. If you have found a nice pattern of handling this, a pull request against graphqlRequestBaseQuery would also be very welcome.

Upvotes: 2

Related Questions