Hadi Norouzi
Hadi Norouzi

Reputation: 199

interceptor for graphql in flutter for handling error

I'm using graphql_flutter package and want to have a interceptor for that, for access token i want to get new token when my request responded with 401 response code.

Upvotes: 8

Views: 4358

Answers (3)

Muhammed Refaat
Muhammed Refaat

Reputation: 9103

GraphQl itself doesn't have such interceptor, and they are not willing to as per this thread. Instead you can use requests_inspector package that can be used with graphql library:

To use requests_inspector with graphql_flutter library. you jus need to wrap your normal HttpLink with our GraphQLInspectorLink and we are done.

final client = GraphQLClient(
    cache: GraphQLCache(),
    link: Link.split(
      (request) => request.isSubscription,
      GraphQLInspectorLink(WebSocketLink('ws://graphqlzero.almansi.me/api')),
      GraphQLInspectorLink(HttpLink('https://graphqlzero.almansi.me/api')),
    ),
  );

But this package seems like they don't provide a callback method, but another weird method to get the request details by "shaking your phone", I'm not sure if there is another descent way to achieve that using this package or no, if not you can fork the same package to achieve your goal.

Upvotes: 1

Alan Bosco
Alan Bosco

Reputation: 837

Inside the (builder:{}) you can use if (result.hasException) {}, this will trigger automatically when you get a error from the graphql. so, do check that error is JWT expire. you can check this by result.exception.toString(). Then you can run the other mutation to refresh to token then re-initialize the client.

Upvotes: 1

Pompidou
Pompidou

Reputation: 607

i think its better to use a global error handler widget that you can call with yout Query widget

here is my example error handler

final _appstate = getIt.get<AppState>();

class ExceptionBuilder extends StatelessWidget {
  final OperationException exception;
  final bool hasData;
  final VoidCallback refetch;
  const ExceptionBuilder(
      {Key key,
      @required this.exception,
      @required this.hasData,
      @required this.refetch})
      : super(key: key);

  Widget _resolver(BuildContext context) {
    if ((exception.linkException is LinkException)) {
      return SliverFillRemaining(
        hasScrollBody: false,
        child: Center(
          child: Column(mainAxisSize: MainAxisSize.min, children: [
            emptyList(context, icon: Icons.wifi_off, msg: "Network Error"),
            FlatButton(
                onPressed: refetch,
                child: Text(
                  "retry",
                  style: TextStyle(color: accentColor),
                ))
          ]),
        ),
      );
    } else if (exception.graphqlErrors.isNotEmpty) {
      List<String> _errors = exception.graphqlErrors[0].message.split(':');

      if (_errors[1] == " JWTExpired") {
        _appstate.refreshToken();
        return SliverFillRemaining(
            hasScrollBody: false,
            child: Container(
              alignment: Alignment.center,
              child: masker(context, Loader()),
            ));
      }
      return SliverFillRemaining(
        hasScrollBody: false,
        child: Column(mainAxisSize: MainAxisSize.min, children: [
          emptyList(context,
              icon: Icons.warning_amber_rounded, msg: "Something went wrong"),
          FlatButton(
              onPressed: refetch,
              child: Text(
                "retry",
                style: TextStyle(color: accentColor),
              ))
        ]),
      );
    }

    return SliverToBoxAdapter(
      child: SizedBox.shrink(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _resolver(context);
  }
}

I am using sliver widget because i am calling it in CustomScrollView

here is the resolver method

  List<Widget> _resolver(BuildContext context, QueryResult result,
      FetchMore fetchMore, Refetch refetch) {
    if (result.isLoading && isNull(result.data)) {
      return [
        SliverFillRemaining(
            hasScrollBody: false,
            child: Container(
              alignment: Alignment.center,
              child: masker(context, Loader()),
            ))
      ];
    }

    if (!isNull(result.data)) {
      List<PersonMiniSchedule> _schedule = scheduleMiniJsonToList(
        result.data['person_max_score_per_schedule'],
      );

      return [
        SliverToBoxAdapter(child: SizedBox(height: 30)),
        _schedule.isEmpty
            ? SliverFillRemaining(
                child: Center(
                    child: emptyList(context,
                        icon: FontAwesomeIcons.book, msg: "No Schedules Yet.")),
              )
            : SliverList(
                delegate: SliverChildBuilderDelegate(
                    (BuildContext context, int index) {
                return ScheduleCard(
                  schedule: _schedule[index],
                );
              }, childCount: _schedule.length)),
      ];
    }

    if (result.hasException) {
      return [
        ExceptionBuilder(
            exception: result.exception,
            hasData: isNull(result.data),
            refetch: refetch)
      ];
    }

    return [
      SliverToBoxAdapter(
        child: SizedBox.shrink(),
      )
    ];
  }

here is the Query widget

Query(
            options: QueryOptions(
                variables: {'id': _appstate.token.hasuraId},
                document: yourQuery()),
            builder: (QueryResult result,
                {VoidCallback refetch, FetchMore fetchMore}) {
              return RefreshIndicator(
                  onRefresh: () async => refetch(),
                  child: CustomScrollView(
                    slivers: [
                      ..._resolver(context, result, fetchMore, refetch),
                      SliverToBoxAdapter(
                          child: SizedBox(
                        height: 200,
                      )),
                    ],
                  ));
            })

Upvotes: 1

Related Questions