You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Redux Toolkit RTK Query GraphQL应用中包装graphqlRequestBaseQuery处理过期令牌

包装graphqlRequestBaseQuery实现令牌自动刷新(TypeScript示例)

以下是基于你现有代码的完整实现,包含TypeScript类型定义和令牌刷新逻辑:

import { createApi, BaseQueryFn } from "@reduxjs/toolkit/query/react";
import { graphqlRequestBaseQuery } from "@rtk-query/graphql-request-base-query";
import { ClientError, GraphQLClient } from "graphql-request";
import { RootState } from "../store";
// 导入你的auth slice action(需自行定义)
import { authActions } from "../features/auth/authSlice";

const baseUrl = process.env.REACT_APP_TECHNOTES_API_HTTP_GRAPHQL;
const techNotesHttpAPI = baseUrl ? baseUrl : "http://localhost:4001/graphql";

// 定义刷新令牌相关类型
interface RefreshTokenResponse {
  refreshToken: {
    accessToken: string;
    refreshToken: string;
  };
}

// 原基础查询
const baseQuery = graphqlRequestBaseQuery<
  Partial<ClientError> & { statusCode: number; error: string }
>({
  url: techNotesHttpAPI,
  prepareHeaders: (headers, { getState }) => {
    const state = getState() as RootState;
    const token = state.auth.token;

    if (token) {
      headers.set("Authorization", `Bearer ${token}`);
    }

    return headers;
  },
  customErrors: ({ name, stack, response }) => {
    const {
      message = "",
      statusCode = 500,
      error = "",
    } = response?.errors?.length
      ? response?.errors[0]?.extensions.response
      : {};

    return {
      name,
      message,
      statusCode,
      error,
      stack,
    };
  },
});

// 带令牌刷新的包装查询
const baseQueryWithReauth: BaseQueryFn<
  Parameters<typeof baseQuery>[0],
  unknown,
  Partial<ClientError> & { statusCode: number; error: string }
> = async (args, api, extraOptions) => {
  let result = await baseQuery(args, api, extraOptions);

  // 捕获访问令牌过期的401错误
  if (result.error?.statusCode === 401) {
    const state = api.getState() as RootState;
    const refreshToken = state.auth.refreshToken;

    if (refreshToken) {
      try {
        // 发送刷新令牌请求(根据你的API调整mutation内容)
        const client = new GraphQLClient(techNotesHttpAPI);
        const refreshResult = await client.request<RefreshTokenResponse>(
          `mutation RefreshToken($refreshToken: String!) {
            refreshToken(refreshToken: $refreshToken) {
              accessToken
              refreshToken
            }
          }`,
          { refreshToken }
        );

        // 更新redux中的令牌状态
        api.dispatch(
          authActions.setCredentials({
            token: refreshResult.refreshToken.accessToken,
            refreshToken: refreshResult.refreshToken.refreshToken,
          })
        );

        // 重试原请求
        result = await baseQuery(args, api, extraOptions);
      } catch (refreshError) {
        // 刷新令牌失效,清除登录状态
        api.dispatch(authActions.logout());
      }
    } else {
      // 无刷新令牌,直接清除登录状态
      api.dispatch(authActions.logout());
    }
  }

  return result;
};

export const apiSlice = createApi({
  reducerPath: "api",
  baseQuery: baseQueryWithReauth, // 使用包装后的查询
  tagTypes: ["User", "Note"],
  endpoints: () => ({}),
});

关键说明

  • 类型匹配:BaseQueryFn的泛型参数严格对应原baseQuery的参数、返回数据、错误类型,确保TypeScript类型校验通过。
  • 错误识别:通过statusCode: 401判断访问令牌过期,可根据你的API错误格式调整判断逻辑。
  • 刷新逻辑:使用graphql-request直接发送刷新mutation(如果你的刷新接口是REST,可替换为fetch或fetchBaseQuery)。
  • 状态同步:获取新令牌后,通过redux action更新全局auth状态,保证后续请求自动使用新令牌。
  • 降级处理:刷新令牌失效时,清除登录状态,引导用户重新登录。

内容的提问来源于stack exchange,提问作者Dwight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 23:41:31