This is an automated email from the ASF dual-hosted git repository.

young pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 40e616779 refactor: standardize request methods (#3075)
40e616779 is described below

commit 40e6167794afb0ebd6c682b8daff32f86c8d6660
Author: YYYoung <isk...@outlook.com>
AuthorDate: Wed May 21 14:03:01 2025 +0800

    refactor: standardize request methods (#3075)
    
    * refactor: use table pagination in all list pages
    
    * feat: gen hooks from factory func
    
    * fix: logic
    
    * fix: detail query options
    
    * fix:
    
    * chore
    
    * fix: credentials params
    
    * fix(useSearchParams): `setParams` type
---
 src/apis/consumer_groups.ts                        |  48 ++---
 src/apis/consumers.ts                              |  40 ++--
 src/apis/credentials.ts                            |  75 ++++---
 src/apis/global_rules.ts                           |  44 ++++
 src/apis/hooks.ts                                  | 221 ++++++++++++++++++---
 src/apis/plugin_configs.ts                         |  42 ++--
 src/apis/plugins.ts                                |  19 --
 src/apis/protos.ts                                 |  43 ++--
 src/apis/routes.ts                                 |   1 -
 src/apis/secrets.ts                                |  55 +++--
 src/apis/services.ts                               |  40 ++--
 src/apis/ssls.ts                                   |  42 ++--
 src/apis/stream_routes.ts                          |  48 ++---
 src/apis/upstreams.ts                              |   1 -
 src/routes/consumer_groups/add.tsx                 |   6 +-
 src/routes/consumer_groups/detail.$id.tsx          |  12 +-
 src/routes/consumer_groups/index.tsx               |  30 +--
 src/routes/consumers/add.tsx                       |   5 +-
 .../consumers/detail.$username/credentials/add.tsx |  12 +-
 .../detail.$username/credentials/detail.$id.tsx    |  21 +-
 .../detail.$username/credentials/index.tsx         |  18 +-
 src/routes/consumers/detail.$username/index.tsx    |   8 +-
 src/routes/consumers/index.tsx                     |  34 +---
 src/routes/global_rules/add.tsx                    |  31 ++-
 src/routes/global_rules/detail.$id.tsx             |  34 ++--
 src/routes/global_rules/index.tsx                  | 122 +++++-------
 src/routes/plugin_configs/add.tsx                  |   6 +-
 src/routes/plugin_configs/detail.$id.tsx           |  18 +-
 src/routes/plugin_configs/index.tsx                |  30 +--
 src/routes/protos/add.tsx                          |  22 +-
 src/routes/protos/detail.$id.tsx                   |  12 +-
 src/routes/protos/index.tsx                        |  28 +--
 src/routes/secrets/add.tsx                         |  12 +-
 src/routes/secrets/detail.$manager.$id.tsx         |  13 +-
 src/routes/secrets/index.tsx                       |  28 +--
 src/routes/services/add.tsx                        |  11 +-
 src/routes/services/detail.$id.tsx                 |  19 +-
 src/routes/services/index.tsx                      |  27 +--
 src/routes/ssls/add.tsx                            |  12 +-
 src/routes/ssls/detail.$id.tsx                     |   9 +-
 src/routes/ssls/index.tsx                          |  28 +--
 src/routes/stream_routes/add.tsx                   |  15 +-
 src/routes/stream_routes/detail.$id.tsx            |  18 +-
 src/routes/stream_routes/index.tsx                 |  27 +--
 src/routes/upstreams/add.tsx                       |   2 +-
 src/routes/upstreams/detail.$id.tsx                |   2 +-
 src/types/schema/pageSearch.ts                     |  51 ++---
 src/utils/usePagination.ts                         | 174 ----------------
 src/utils/useSearchParams.ts                       |  24 ++-
 src/utils/useTablePagination.ts                    |  19 +-
 50 files changed, 682 insertions(+), 977 deletions(-)

diff --git a/src/apis/consumer_groups.ts b/src/apis/consumer_groups.ts
index 189e46a88..f7f73864e 100644
--- a/src/apis/consumer_groups.ts
+++ b/src/apis/consumer_groups.ts
@@ -14,41 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_CONSUMER_GROUPS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getConsumerGroupListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['consumer_groups', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespConsumerGroupList']>(
-          API_CONSUMER_GROUPS,
-          {
-            params: { page, page_size: pageSize },
-          }
-        )
-        .then((v) => v.data),
-  });
-};
+export const getConsumerGroupListReq = (
+  req: AxiosInstance,
+  params: PageSearchType
+) =>
+  req
+    .get<unknown, APISIXType['RespConsumerGroupList']>(API_CONSUMER_GROUPS, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getConsumerGroupQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['consumer_group', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespConsumerGroupDetail']>(
-          `${API_CONSUMER_GROUPS}/${id}`
-        )
-        .then((v) => v.data),
-  });
+export const getConsumerGroupReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespConsumerGroupDetail']>(
+      `${API_CONSUMER_GROUPS}/${id}`
+    )
+    .then((v) => v.data);
 
-export const putConsumerGroupReq = (data: APISIXType['ConsumerGroupPut']) => {
+export const putConsumerGroupReq = (
+  req: AxiosInstance,
+  data: APISIXType['ConsumerGroupPut']
+) => {
   const { id, ...rest } = data;
   return req.put<
     APISIXType['ConsumerGroupPut'],
diff --git a/src/apis/consumers.ts b/src/apis/consumers.ts
index f19aa1831..c4779df1c 100644
--- a/src/apis/consumers.ts
+++ b/src/apis/consumers.ts
@@ -14,36 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_CONSUMERS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getConsumerListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['consumers', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespConsumerList']>(API_CONSUMERS, {
-          params: { page, page_size: pageSize },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getConsumerListReq = (req: AxiosInstance, params: PageSearchType) 
=>
+  req
+    .get<unknown, APISIXType['RespConsumerList']>(API_CONSUMERS, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getConsumerQueryOptions = (username: string) =>
-  queryOptions({
-    queryKey: ['consumer', username],
-    queryFn: () =>
-      req
-        .get<unknown, 
APISIXType['RespConsumerDetail']>(`${API_CONSUMERS}/${username}`)
-        .then((v) => v.data),
-  });
+export const getConsumerReq = (req: AxiosInstance, username: string) =>
+  req
+    .get<unknown, APISIXType['RespConsumerDetail']>(
+      `${API_CONSUMERS}/${username}`
+    )
+    .then((v) => v.data);
 
-export const putConsumerReq = (data: APISIXType['ConsumerPut']) => {
+export const putConsumerReq = (
+  req: AxiosInstance,
+  data: APISIXType['ConsumerPut']
+) => {
   return req.put<APISIXType['ConsumerPut'], APISIXType['RespConsumerDetail']>(
     API_CONSUMERS,
     data
diff --git a/src/apis/credentials.ts b/src/apis/credentials.ts
index 94537171b..574459825 100644
--- a/src/apis/credentials.ts
+++ b/src/apis/credentials.ts
@@ -14,55 +14,48 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+
+import type { AxiosInstance } from 'axios';
 
 import { API_CREDENTIALS, SKIP_INTERCEPTOR_HEADER } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { APISIXListResponse } from '@/types/schema/apisix/type';
 
-type WithUsername = Pick<APISIXType['Consumer'], 'username'>;
-export const getCredentialListQueryOptions = (props: WithUsername) => {
-  const { username } = props;
-  return queryOptions({
-    queryKey: ['credentials', username],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespCredentialList']>(
-          API_CREDENTIALS(username),
-          {
-            headers: {
-              [SKIP_INTERCEPTOR_HEADER]: ['404'],
-            },
-          }
-        )
-        .then((v) => v.data)
-        .catch((e) => {
-          // 404 means credentials is empty
-          if (e.response.status === 404) {
-            const res: APISIXListResponse<APISIXType['Credential']> = {
-              total: 0,
-              list: [],
-            };
-            return res;
-          }
-          throw e;
-        }),
-  });
-};
+export type WithUsername = Pick<APISIXType['Consumer'], 'username'>;
+
+export const getCredentialListReq = (req: AxiosInstance, params: WithUsername) 
=>
+  req
+    .get<unknown, APISIXType['RespCredentialList']>(
+      API_CREDENTIALS(params.username),
+      {
+        headers: {
+          [SKIP_INTERCEPTOR_HEADER]: ['404'],
+        },
+        params,
+      }
+    )
+    .then((v) => v.data)
+    .catch((e) => {
+      // 404 means credentials is empty
+      if (e.response.status === 404) {
+        const res: APISIXListResponse<APISIXType['Credential']> = {
+          total: 0,
+          list: [],
+        };
+        return res;
+      }
+      throw e;
+    });
 
-export const getCredentialQueryOptions = (username: string, id: string) =>
-  queryOptions({
-    queryKey: ['credential', username, id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespCredentialDetail']>(
-          `${API_CREDENTIALS(username)}/${id}`
-        )
-        .then((v) => v.data),
-  });
+export const getCredentialReq = (req: AxiosInstance, username: string, id: 
string) =>
+  req
+    .get<unknown, APISIXType['RespCredentialDetail']>(
+      `${API_CREDENTIALS(username)}/${id}`
+    )
+    .then((v) => v.data);
 
 export const putCredentialReq = (
+  req: AxiosInstance,
   data: APISIXType['CredentialPut'] & WithUsername
 ) => {
   const { username, id, ...rest } = data;
diff --git a/src/apis/global_rules.ts b/src/apis/global_rules.ts
new file mode 100644
index 000000000..4572f7ad0
--- /dev/null
+++ b/src/apis/global_rules.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { AxiosInstance } from 'axios';
+
+import { API_GLOBAL_RULES } from '@/config/constant';
+import type { APISIXType } from '@/types/schema/apisix';
+
+export const getGlobalRuleListReq = (req: AxiosInstance) =>
+  req
+    .get<unknown, APISIXType['RespGlobalRuleList']>(API_GLOBAL_RULES)
+    .then((v) => v.data);
+
+export const getGlobalRuleReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespGlobalRuleDetail']>(
+      `${API_GLOBAL_RULES}/${id}`
+    )
+    .then((v) => v.data);
+
+export const putGlobalRuleReq = (
+  req: AxiosInstance,
+  data: APISIXType['GlobalRulePut']
+) => {
+  const { id, ...rest } = data;
+  return req.put<
+    APISIXType['GlobalRulePut'],
+    APISIXType['RespGlobalRuleDetail']
+  >(`${API_GLOBAL_RULES}/${id}`, rest);
+};
diff --git a/src/apis/hooks.ts b/src/apis/hooks.ts
index f1e6d569a..118fa6b12 100644
--- a/src/apis/hooks.ts
+++ b/src/apis/hooks.ts
@@ -15,47 +15,208 @@
  * limitations under the License.
  */
 import { queryOptions, useSuspenseQuery } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
-import { getUpstreamListReq } from '@/apis/upstreams';
+import { getRouteListReq, getRouteReq } from '@/apis/routes';
+import { getUpstreamListReq, getUpstreamReq } from '@/apis/upstreams';
 import { req } from '@/config/req';
+import type {
+  APISIXDetailResponse,
+  APISIXListResponse,
+} from '@/types/schema/apisix/type';
 import { type PageSearchType } from '@/types/schema/pageSearch';
 import { useSearchParams } from '@/utils/useSearchParams';
-import { useTablePagination } from '@/utils/useTablePagination';
+import {
+  type ListPageKeys,
+  useTablePagination,
+} from '@/utils/useTablePagination';
 
-import { getRouteListReq, getRouteReq } from './routes';
+import {
+  getConsumerGroupListReq,
+  getConsumerGroupReq,
+} from './consumer_groups';
+import { getConsumerListReq, getConsumerReq } from './consumers';
+import { getCredentialListReq, getCredentialReq } from './credentials';
+import { getGlobalRuleListReq, getGlobalRuleReq } from './global_rules';
+import { getPluginConfigListReq, getPluginConfigReq } from './plugin_configs';
+import { getProtoListReq, getProtoReq } from './protos';
+import { getSecretListReq, getSecretReq } from './secrets';
+import { getServiceListReq, getServiceReq } from './services';
+import { getSSLListReq, getSSLReq } from './ssls';
+import { getStreamRouteListReq, getStreamRouteReq } from './stream_routes';
 
-export const getUpstreamListQueryOptions = (props: PageSearchType) => {
-  return queryOptions({
-    queryKey: ['upstreams', props.page, props.page_size],
-    queryFn: () => getUpstreamListReq(req, props),
-  });
-};
+const genDetailQueryOptions =
+  <T extends unknown[], R>(
+    key: string,
+    getDetailReq: (
+      req: AxiosInstance,
+      ...args: T
+    ) => Promise<APISIXDetailResponse<R>>
+  ) =>
+  (...args: T) => {
+    return queryOptions({
+      queryKey: [key, ...args],
+      queryFn: () => getDetailReq(req, ...args),
+    });
+  };
+/** simple factory func for list query options which support extends 
PageSearchType */
+const genListQueryOptions =
+  <P extends PageSearchType, R>(
+    key: string,
+    listReq: (req: AxiosInstance, props: P) => Promise<APISIXListResponse<R>>
+  ) =>
+  (props: P) => {
+    return queryOptions({
+      queryKey: [key, props],
+      queryFn: () => listReq(req, props),
+    });
+  };
 
-export const useUpstreamList = () => {
-  const { params, setParams } = useSearchParams('/upstreams/');
-  const upstreamQuery = useSuspenseQuery(getUpstreamListQueryOptions(params));
-  const { data, isLoading, refetch } = upstreamQuery;
-  const pagination = useTablePagination({ data, setParams, params });
-  return { data, isLoading, refetch, pagination };
+/** simple hook factory func for list hooks which support extends 
PageSearchType */
+export const genUseList = <T extends ListPageKeys, P extends PageSearchType, 
R>(
+  routeId: T,
+  listQueryOptions: ReturnType<typeof genListQueryOptions<P, R>>
+) => {
+  return () => {
+    const { params, setParams } = useSearchParams<T, P>(routeId);
+    const listQuery = useSuspenseQuery(listQueryOptions(params));
+    const { data, isLoading, refetch } = listQuery;
+    const opts = { data, setParams, params };
+    const pagination = useTablePagination(opts);
+    return { data, isLoading, refetch, pagination };
+  };
 };
 
-export const getRouteListQueryOptions = (props: PageSearchType) => {
+export const getUpstreamQueryOptions = genDetailQueryOptions(
+  'upstream',
+  getUpstreamReq
+);
+export const getUpstreamListQueryOptions = genListQueryOptions(
+  'upstreams',
+  getUpstreamListReq
+);
+export const useUpstreamList = genUseList(
+  '/upstreams/',
+  getUpstreamListQueryOptions
+);
+
+export const getRouteQueryOptions = genDetailQueryOptions('route', 
getRouteReq);
+export const getRouteListQueryOptions = genListQueryOptions(
+  'routes',
+  getRouteListReq
+);
+export const useRouteList = genUseList('/routes/', getRouteListQueryOptions);
+
+export const getConsumerGroupQueryOptions = genDetailQueryOptions(
+  'consumer_group',
+  getConsumerGroupReq
+);
+export const getConsumerGroupListQueryOptions = genListQueryOptions(
+  'consumer_groups',
+  getConsumerGroupListReq
+);
+export const useConsumerGroupList = genUseList(
+  '/consumer_groups/',
+  getConsumerGroupListQueryOptions
+);
+
+export const getStreamRouteQueryOptions = genDetailQueryOptions(
+  'stream_route',
+  getStreamRouteReq
+);
+export const getStreamRouteListQueryOptions = genListQueryOptions(
+  'stream_routes',
+  getStreamRouteListReq
+);
+export const useStreamRouteList = genUseList(
+  '/stream_routes/',
+  getStreamRouteListQueryOptions
+);
+
+export const getServiceQueryOptions = genDetailQueryOptions(
+  'service',
+  getServiceReq
+);
+export const getServiceListQueryOptions = genListQueryOptions(
+  'services',
+  getServiceListReq
+);
+export const useServiceList = genUseList(
+  '/services/',
+  getServiceListQueryOptions
+);
+
+export const getGlobalRuleQueryOptions = genDetailQueryOptions(
+  'global_rule',
+  getGlobalRuleReq
+);
+export const getGlobalRuleListQueryOptions = genListQueryOptions(
+  'global_rules',
+  getGlobalRuleListReq
+);
+export const useGlobalRuleList = genUseList(
+  '/global_rules/',
+  getGlobalRuleListQueryOptions
+);
+
+export const getPluginConfigQueryOptions = genDetailQueryOptions(
+  'plugin_config',
+  getPluginConfigReq
+);
+export const getPluginConfigListQueryOptions = genListQueryOptions(
+  'plugin_configs',
+  getPluginConfigListReq
+);
+export const usePluginConfigList = genUseList(
+  '/plugin_configs/',
+  getPluginConfigListQueryOptions
+);
+
+export const getSSLQueryOptions = genDetailQueryOptions('ssl', getSSLReq);
+export const getSSLListQueryOptions = genListQueryOptions('ssls', 
getSSLListReq);
+export const useSSLList = genUseList('/ssls/', getSSLListQueryOptions);
+
+export const getConsumerQueryOptions = genDetailQueryOptions(
+  'consumer',
+  getConsumerReq
+);
+export const getConsumerListQueryOptions = genListQueryOptions(
+  'consumers',
+  getConsumerListReq
+);
+export const useConsumerList = genUseList(
+  '/consumers/',
+  getConsumerListQueryOptions
+);
+
+export const getCredentialQueryOptions = genDetailQueryOptions(
+  'credential',
+  getCredentialReq
+);
+export const getCredentialListQueryOptions = (username: string) => {
   return queryOptions({
-    queryKey: ['routes', props.page, props.page_size],
-    queryFn: () => getRouteListReq(req, props),
+    queryKey: ['credentials', username],
+    queryFn: () => getCredentialListReq(req, { username }),
   });
 };
-
-export const useRouteList = () => {
-  const { params, setParams } = useSearchParams('/routes/');
-  const routeQuery = useSuspenseQuery(getRouteListQueryOptions(params));
-  const { data, isLoading, refetch } = routeQuery;
-  const pagination = useTablePagination({ data, setParams, params });
-  return { data, isLoading, refetch, pagination };
+export const useCredentialsList = (username: string) => {
+  const credentialQuery = useSuspenseQuery(
+    getCredentialListQueryOptions(username)
+  );
+  const { data, isLoading, refetch } = credentialQuery;
+  return { data, isLoading, refetch };
 };
 
-export const getRouteQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['route', id],
-    queryFn: () => getRouteReq(req, id),
-  });
+export const getProtoQueryOptions = genDetailQueryOptions('proto', 
getProtoReq);
+export const getProtoListQueryOptions = genListQueryOptions('protos', 
getProtoListReq);
+export const useProtoList = genUseList('/protos/', getProtoListQueryOptions);
+
+export const getSecretQueryOptions = genDetailQueryOptions(
+  'secret',
+  getSecretReq
+);
+export const getSecretListQueryOptions = genListQueryOptions(
+  'secrets',
+  getSecretListReq
+);
+export const useSecretList = genUseList('/secrets/', 
getSecretListQueryOptions);
diff --git a/src/apis/plugin_configs.ts b/src/apis/plugin_configs.ts
index b873bc285..41d3d6a1f 100644
--- a/src/apis/plugin_configs.ts
+++ b/src/apis/plugin_configs.ts
@@ -14,38 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_PLUGIN_CONFIGS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getPluginConfigListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['plugin_configs', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespPluginConfigList']>(API_PLUGIN_CONFIGS, {
-          params: { page, page_size: pageSize },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getPluginConfigListReq = (req: AxiosInstance, params: 
PageSearchType) =>
+  req
+    .get<unknown, APISIXType['RespPluginConfigList']>(API_PLUGIN_CONFIGS, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getPluginConfigQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['plugin_config', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespPluginConfigDetail']>(
-          `${API_PLUGIN_CONFIGS}/${id}`
-        )
-        .then((v) => v.data),
-  });
+export const getPluginConfigReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespPluginConfigDetail']>(
+      `${API_PLUGIN_CONFIGS}/${id}`
+    )
+    .then((v) => v.data);
 
-export const putPluginConfigReq = (data: APISIXType['PluginConfigPut']) => {
+export const putPluginConfigReq = (
+  req: AxiosInstance,
+  data: APISIXType['PluginConfigPut']
+) => {
   const { id, ...rest } = data;
   return req.put<
     APISIXType['PluginConfigPut'],
diff --git a/src/apis/plugins.ts b/src/apis/plugins.ts
index 7fcb9cc2c..8e14b343c 100644
--- a/src/apis/plugins.ts
+++ b/src/apis/plugins.ts
@@ -19,7 +19,6 @@ import type { AxiosRequestConfig } from 'axios';
 
 import type { PluginConfig } from 
'@/components/form-slice/FormItemPlugins/PluginEditorDrawer';
 import {
-  API_GLOBAL_RULES,
   API_PLUGIN_METADATA,
   API_PLUGINS,
   API_PLUGINS_LIST,
@@ -27,24 +26,6 @@ import {
 import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 
-export const putGlobalRuleReq = (data: APISIXType['GlobalRulePut']) => {
-  const { id, ...rest } = data;
-  return req.put<
-    APISIXType['GlobalRulePut'],
-    APISIXType['RespGlobalRuleDetail']
-  >(`${API_GLOBAL_RULES}/${id}`, rest);
-};
-
-export const getGlobalRuleQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['global_rule', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespGlobalRuleDetail']>(
-          `${API_GLOBAL_RULES}/${id}`
-        )
-        .then((v) => v.data),
-  });
 
 export type NeedPluginSchema = {
   schema: APISIXType['PluginSchemaKeys'];
diff --git a/src/apis/protos.ts b/src/apis/protos.ts
index 352e80209..fe0e93d46 100644
--- a/src/apis/protos.ts
+++ b/src/apis/protos.ts
@@ -14,39 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_PROTOS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getProtoListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['protos', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespProtoList']>(API_PROTOS, {
-          params: {
-            page,
-            page_size: pageSize,
-          },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getProtoListReq = (req: AxiosInstance, params: PageSearchType) =>
+  req
+    .get<unknown, APISIXType['RespProtoList']>(API_PROTOS, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getProtoQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['proto', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespProtoDetail']>(`${API_PROTOS}/${id}`)
-        .then((v) => v.data),
-  });
+export const getProtoReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespProtoDetail']>(`${API_PROTOS}/${id}`)
+    .then((v) => v.data);
 
-export const putProtoReq = (data: APISIXType['Proto']) => {
+export const putProtoReq = (req: AxiosInstance, data: APISIXType['Proto']) => {
   const { id, ...rest } = data;
   return req.put<APISIXType['Proto'], APISIXType['RespProtoDetail']>(
     `${API_PROTOS}/${id}`,
@@ -54,7 +40,10 @@ export const putProtoReq = (data: APISIXType['Proto']) => {
   );
 };
 
-export const postProtoReq = (data: APISIXType['ProtoPost']) => {
+export const postProtoReq = (
+  req: AxiosInstance,
+  data: APISIXType['ProtoPost']
+) => {
   return req.post<APISIXType['ProtoPost'], APISIXType['RespProtoList']>(
     API_PROTOS,
     data
diff --git a/src/apis/routes.ts b/src/apis/routes.ts
index c541bf70b..2eeadacc7 100644
--- a/src/apis/routes.ts
+++ b/src/apis/routes.ts
@@ -46,7 +46,6 @@ export const deleteAllRoutes = async (req: AxiosInstance) => {
   const res = await getRouteListReq(req, {
     page: 1,
     page_size: 1000,
-    pageSize: 1000,
   });
   if (res.total === 0) return;
   return await Promise.all(
diff --git a/src/apis/secrets.ts b/src/apis/secrets.ts
index d3f5e4070..46ec64349 100644
--- a/src/apis/secrets.ts
+++ b/src/apis/secrets.ts
@@ -14,10 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_SECRETS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
@@ -37,41 +36,35 @@ export const preParseSecretItem = <T extends 
APISIXType['RespSecretItem']>(
   return { ...data, value: { ...data.value, manager, id: realId } };
 };
 
-export const getSecretListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['secrets', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespSecretList']>(API_SECRETS, {
-          params: { page, page_size: pageSize },
-        })
-        .then((v) => {
-          const { list, ...rest } = v.data;
-          return {
-            ...rest,
-            list: list.map(preParseSecretItem),
-          };
-        }),
-  });
-};
+export const getSecretListReq = (req: AxiosInstance, params: PageSearchType) =>
+  req
+    .get<unknown, APISIXType['RespSecretList']>(API_SECRETS, {
+      params,
+    })
+    .then((v) => {
+      const { list, ...rest } = v.data;
+      return {
+        ...rest,
+        list: list.map(preParseSecretItem),
+      };
+    });
 
-export const getSecretQueryOptions = (
+export const getSecretReq = (
+  req: AxiosInstance,
   props: Pick<APISIXType['Secret'], 'id' | 'manager'>
 ) => {
   const { id, manager } = props;
-  return queryOptions({
-    queryKey: ['secret', manager, id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespSecretDetail']>(
-          `${API_SECRETS}/${manager}/${id}`
-        )
-        .then((v) => preParseSecretItem(v.data)),
-  });
+  return req
+    .get<unknown, APISIXType['RespSecretDetail']>(
+      `${API_SECRETS}/${manager}/${id}`
+    )
+    .then((v) => preParseSecretItem(v.data));
 };
 
-export const putSecretReq = (data: APISIXType['Secret']) => {
+export const putSecretReq = (
+  req: AxiosInstance,
+  data: APISIXType['Secret']
+) => {
   const { manager, id, ...rest } = data;
   return req.put<APISIXType['Secret'], APISIXType['RespSecretDetail']>(
     `${API_SECRETS}/${manager}/${id}`,
diff --git a/src/apis/services.ts b/src/apis/services.ts
index 5918879db..dd7eb9aa6 100644
--- a/src/apis/services.ts
+++ b/src/apis/services.ts
@@ -14,38 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
 import { API_SERVICES } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
 export type ServicePostType = APISIXType['ServicePost'];
 
-export const getServiceListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['services', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespServiceList']>(API_SERVICES, {
-          params: { page, page_size: pageSize },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getServiceListReq = (req: AxiosInstance, params: PageSearchType) 
=>
+  req
+    .get<unknown, APISIXType['RespServiceList']>(API_SERVICES, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getServiceQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['service', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespServiceDetail']>(`${API_SERVICES}/${id}`)
-        .then((v) => v.data),
-  });
+export const getServiceReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespServiceDetail']>(`${API_SERVICES}/${id}`)
+    .then((v) => v.data);
 
-export const putServiceReq = (data: APISIXType['Service']) => {
+export const putServiceReq = (
+  req: AxiosInstance,
+  data: APISIXType['Service']
+) => {
   const { id, ...rest } = data;
   return req.put<APISIXType['Service'], APISIXType['RespServiceDetail']>(
     `${API_SERVICES}/${id}`,
@@ -53,7 +45,7 @@ export const putServiceReq = (data: APISIXType['Service']) => 
{
   );
 };
 
-export const postServiceReq = (data: ServicePostType) =>
+export const postServiceReq = (req: AxiosInstance, data: ServicePostType) =>
   req.post<ServicePostType, APISIXType['RespServiceDetail']>(
     API_SERVICES,
     data
diff --git a/src/apis/ssls.ts b/src/apis/ssls.ts
index 89e3cafff..4499fd8ea 100644
--- a/src/apis/ssls.ts
+++ b/src/apis/ssls.ts
@@ -14,42 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+import type { AxiosInstance } from 'axios';
 
+import type { SSLPostType } from '@/components/form-slice/FormPartSSL/schema';
 import { API_SSLS } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getSSLListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['ssls', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespSSLList']>(API_SSLS, {
-          params: {
-            page,
-            page_size: pageSize,
-          },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getSSLListReq = (req: AxiosInstance, params: PageSearchType) =>
+  req
+    .get<unknown, APISIXType['RespSSLList']>(API_SSLS, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getSSLDetailQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['ssl', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespSSLDetail']>(`${API_SSLS}/${id}`)
-        .then((v) => v.data),
-  });
+export const getSSLReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespSSLDetail']>(`${API_SSLS}/${id}`)
+    .then((v) => v.data);
 
-export const putSSLReq = (data: APISIXType['SSL']) => {
+export const putSSLReq = (req: AxiosInstance, data: APISIXType['SSL']) => {
   const { id, ...rest } = data;
   return req.put<APISIXType['SSL'], APISIXType['RespSSLDetail']>(
     `${API_SSLS}/${id}`,
     rest
   );
 };
+
+export const postSSLReq = (req: AxiosInstance, data: SSLPostType) =>
+  req.post<APISIXType['SSL'], APISIXType['RespSSLDetail']>(API_SSLS, data);
diff --git a/src/apis/stream_routes.ts b/src/apis/stream_routes.ts
index 2f57af4e2..0e26ae225 100644
--- a/src/apis/stream_routes.ts
+++ b/src/apis/stream_routes.ts
@@ -14,39 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { queryOptions } from '@tanstack/react-query';
+
+import type { AxiosInstance } from 'axios';
 
 import type { StreamRoutePostType } from 
'@/components/form-slice/FormPartStreamRoute/schema';
 import { API_STREAM_ROUTES } from '@/config/constant';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import type { PageSearchType } from '@/types/schema/pageSearch';
 
-export const getStreamRouteListQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['stream_routes', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespStreamRouteList']>(API_STREAM_ROUTES, {
-          params: { page, page_size: pageSize },
-        })
-        .then((v) => v.data),
-  });
-};
+export const getStreamRouteListReq = (req: AxiosInstance, params: 
PageSearchType) =>
+  req
+    .get<unknown, APISIXType['RespStreamRouteList']>(API_STREAM_ROUTES, {
+      params,
+    })
+    .then((v) => v.data);
 
-export const getStreamRouteQueryOptions = (id: string) =>
-  queryOptions({
-    queryKey: ['stream_route', id],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespStreamRouteDetail']>(
-          `${API_STREAM_ROUTES}/${id}`
-        )
-        .then((v) => v.data),
-  });
+export const getStreamRouteReq = (req: AxiosInstance, id: string) =>
+  req
+    .get<unknown, APISIXType['RespStreamRouteDetail']>(
+      `${API_STREAM_ROUTES}/${id}`
+    )
+    .then((v) => v.data);
 
-export const putStreamRouteReq = (data: APISIXType['StreamRoute']) => {
+export const putStreamRouteReq = (
+  req: AxiosInstance,
+  data: APISIXType['StreamRoute']
+) => {
   const { id, ...rest } = data;
   return req.put<
     APISIXType['StreamRoute'],
@@ -54,7 +47,10 @@ export const putStreamRouteReq = (data: 
APISIXType['StreamRoute']) => {
   >(`${API_STREAM_ROUTES}/${id}`, rest);
 };
 
-export const postStreamRouteReq = (data: StreamRoutePostType) =>
+export const postStreamRouteReq = (
+  req: AxiosInstance,
+  data: StreamRoutePostType
+) =>
   req.post<unknown, APISIXType['RespStreamRouteDetail']>(
     API_STREAM_ROUTES,
     data
diff --git a/src/apis/upstreams.ts b/src/apis/upstreams.ts
index 1938b3828..a7f484834 100644
--- a/src/apis/upstreams.ts
+++ b/src/apis/upstreams.ts
@@ -55,7 +55,6 @@ export const deleteAllUpstreams = async (req: AxiosInstance) 
=> {
   const res = await getUpstreamListReq(req, {
     page: 1,
     page_size: 1000,
-    pageSize: 1000,
   });
   if (res.total === 0) return;
   return await Promise.all(
diff --git a/src/routes/consumer_groups/add.tsx 
b/src/routes/consumer_groups/add.tsx
index da57d00b1..1b09c9a44 100644
--- a/src/routes/consumer_groups/add.tsx
+++ b/src/routes/consumer_groups/add.tsx
@@ -27,7 +27,8 @@ import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartPluginConfig } from 
'@/components/form-slice/FormPartPluginConfig';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const ConsumerGroupAddForm = () => {
@@ -35,7 +36,8 @@ const ConsumerGroupAddForm = () => {
   const router = useRouter();
 
   const putConsumerGroup = useMutation({
-    mutationFn: putConsumerGroupReq,
+    mutationFn: (d: APISIXType['ConsumerGroupPut']) =>
+      putConsumerGroupReq(req, d),
     async onSuccess(response) {
       notifications.show({
         message: t('info.add.success', { name: t('consumerGroups.singular') }),
diff --git a/src/routes/consumer_groups/detail.$id.tsx 
b/src/routes/consumer_groups/detail.$id.tsx
index 497c1008e..c306a226b 100644
--- a/src/routes/consumer_groups/detail.$id.tsx
+++ b/src/routes/consumer_groups/detail.$id.tsx
@@ -28,17 +28,16 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import {
-  getConsumerGroupQueryOptions,
-  putConsumerGroupReq,
-} from '@/apis/consumer_groups';
+import { putConsumerGroupReq } from '@/apis/consumer_groups';
+import { getConsumerGroupQueryOptions } from '@/apis/hooks';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartPluginConfig } from 
'@/components/form-slice/FormPartPluginConfig';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_CONSUMER_GROUPS } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -55,7 +54,8 @@ const ConsumerGroupDetailForm = (props: Props) => {
   const { data } = consumerGroupQuery;
 
   const putConsumerGroup = useMutation({
-    mutationFn: putConsumerGroupReq,
+    mutationFn: (d: APISIXType['ConsumerGroupPut']) =>
+      putConsumerGroupReq(req, d),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('consumerGroups.singular') 
}),
diff --git a/src/routes/consumer_groups/index.tsx 
b/src/routes/consumer_groups/index.tsx
index 747df0be2..108ce06b6 100644
--- a/src/routes/consumer_groups/index.tsx
+++ b/src/routes/consumer_groups/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getConsumerGroupListQueryOptions } from '@/apis/consumer_groups';
+import { getConsumerGroupListQueryOptions, useConsumerGroupList } from 
'@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,25 +29,10 @@ import { API_CONSUMER_GROUPS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 function ConsumerGroupsList() {
   const { t } = useTranslation();
-
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'consumer_groups',
-  });
-
-  const consumerGroupsQuery = useSuspenseQuery(
-    getConsumerGroupListQueryOptions(pagination)
-  );
-  const { data, isLoading, refetch } = consumerGroupsQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = useConsumerGroupList();
 
   const columns = useMemo<
     ProColumns<APISIXType['RespConsumerGroupItem']>[]
@@ -115,13 +99,7 @@ function ConsumerGroupsList() {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/consumers/add.tsx b/src/routes/consumers/add.tsx
index c7e8adb72..137cf6936 100644
--- a/src/routes/consumers/add.tsx
+++ b/src/routes/consumers/add.tsx
@@ -26,7 +26,8 @@ import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartConsumer } from '@/components/form-slice/FormPartConsumer';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const ConsumerAddForm = () => {
@@ -34,7 +35,7 @@ const ConsumerAddForm = () => {
   const router = useRouter();
 
   const putConsumer = useMutation({
-    mutationFn: putConsumerReq,
+    mutationFn: (d: APISIXType['ConsumerPut']) => putConsumerReq(req, d),
     async onSuccess(_, res) {
       notifications.show({
         message: t('info.add.success', { name: t('consumers.singular') }),
diff --git a/src/routes/consumers/detail.$username/credentials/add.tsx 
b/src/routes/consumers/detail.$username/credentials/add.tsx
index f157bd89b..d5f3dd844 100644
--- a/src/routes/consumers/detail.$username/credentials/add.tsx
+++ b/src/routes/consumers/detail.$username/credentials/add.tsx
@@ -28,7 +28,8 @@ import { FormPartCredential } from 
'@/components/form-slice/FormPartCredential';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
 import { DetailCredentialsTabs } from 
'@/components/page-slice/consumers/DetailCredentialsTabs';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const CredentialAddForm = () => {
@@ -39,7 +40,8 @@ const CredentialAddForm = () => {
   });
 
   const putCredential = useMutation({
-    mutationFn: putCredentialReq,
+    mutationFn: (d: APISIXType['CredentialPut']) =>
+      putCredentialReq(req, pipeProduce()({ ...d, username })),
     async onSuccess(_, res) {
       notifications.show({
         message: t('info.add.success', {
@@ -66,11 +68,7 @@ const CredentialAddForm = () => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          putCredential.mutateAsync({ username, ...pipeProduce()(d) })
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => putCredential.mutateAsync(d))}>
         <FormPartCredential />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/consumers/detail.$username/credentials/detail.$id.tsx 
b/src/routes/consumers/detail.$username/credentials/detail.$id.tsx
index f13235aa0..d848bcea0 100644
--- a/src/routes/consumers/detail.$username/credentials/detail.$id.tsx
+++ b/src/routes/consumers/detail.$username/credentials/detail.$id.tsx
@@ -28,10 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import {
-  getCredentialQueryOptions,
-  putCredentialReq,
-} from '@/apis/credentials';
+import { putCredentialReq } from '@/apis/credentials';
+import { getCredentialQueryOptions } from '@/apis/hooks';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartCredential } from 
'@/components/form-slice/FormPartCredential';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -39,7 +37,8 @@ import { DeleteResourceBtn } from 
'@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { DetailCredentialsTabs } from 
'@/components/page-slice/consumers/DetailCredentialsTabs';
 import { API_CREDENTIALS } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type CredentialFormProps = {
@@ -75,7 +74,8 @@ const CredentialDetailForm = (props: CredentialFormProps) => {
   }, [credentialData, form, isLoading]);
 
   const putCredential = useMutation({
-    mutationFn: putCredentialReq,
+    mutationFn: (d: APISIXType['CredentialPut']) =>
+      putCredentialReq(req, pipeProduce()({ ...d, username })),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('credentials.singular') }),
@@ -92,14 +92,7 @@ const CredentialDetailForm = (props: CredentialFormProps) => 
{
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) => {
-          putCredential.mutateAsync({
-            username,
-            ...pipeProduce()(d),
-          });
-        })}
-      >
+      <form onSubmit={form.handleSubmit((d) => putCredential.mutateAsync(d))}>
         <FormPartCredential showDate />
         {!readOnly && (
           <Group>
diff --git a/src/routes/consumers/detail.$username/credentials/index.tsx 
b/src/routes/consumers/detail.$username/credentials/index.tsx
index ff0291967..d331ec245 100644
--- a/src/routes/consumers/detail.$username/credentials/index.tsx
+++ b/src/routes/consumers/detail.$username/credentials/index.tsx
@@ -16,12 +16,14 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute, useParams } from '@tanstack/react-router';
 import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getCredentialListQueryOptions } from '@/apis/credentials';
+import {
+  getCredentialListQueryOptions,
+  useCredentialsList,
+} from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -36,11 +38,7 @@ function CredentialsList() {
   const { username } = useParams({
     from: '/consumers/detail/$username/credentials/',
   });
-
-  const credentialsQuery = useSuspenseQuery(
-    getCredentialListQueryOptions({ username })
-  );
-  const { data, isLoading, refetch } = credentialsQuery;
+  const { data, isLoading, refetch } = useCredentialsList(username);
 
   const columns = useMemo<
     ProColumns<APISIXType['RespCredentialItem']>[]
@@ -142,10 +140,8 @@ function RouteComponent() {
   );
 }
 
-export const Route = createFileRoute(
-  '/consumers/detail/$username/credentials/'
-)({
+export const Route = 
createFileRoute('/consumers/detail/$username/credentials/')({
   component: RouteComponent,
   loader: ({ params }) =>
-    queryClient.ensureQueryData(getCredentialListQueryOptions(params)),
+    
queryClient.ensureQueryData(getCredentialListQueryOptions(params.username)),
 });
diff --git a/src/routes/consumers/detail.$username/index.tsx 
b/src/routes/consumers/detail.$username/index.tsx
index b44ec4150..e7585dded 100644
--- a/src/routes/consumers/detail.$username/index.tsx
+++ b/src/routes/consumers/detail.$username/index.tsx
@@ -28,7 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getConsumerQueryOptions, putConsumerReq } from '@/apis/consumers';
+import { putConsumerReq } from '@/apis/consumers';
+import { getConsumerQueryOptions } from '@/apis/hooks';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartConsumer } from '@/components/form-slice/FormPartConsumer';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -37,7 +38,8 @@ import { DeleteResourceBtn } from 
'@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { DetailCredentialsTabs } from 
'@/components/page-slice/consumers/DetailCredentialsTabs';
 import { API_CONSUMERS } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -68,7 +70,7 @@ const ConsumerDetailForm = (props: Props) => {
   }, [consumerData, form, isLoading]);
 
   const putConsumer = useMutation({
-    mutationFn: putConsumerReq,
+    mutationFn: (d: APISIXType['ConsumerPut']) => putConsumerReq(req, d),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('consumers.singular') }),
diff --git a/src/routes/consumers/index.tsx b/src/routes/consumers/index.tsx
index 2663e2496..b431ed3b4 100644
--- a/src/routes/consumers/index.tsx
+++ b/src/routes/consumers/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getConsumerListQueryOptions } from '@/apis/consumers';
+import { getConsumerListQueryOptions, useConsumerList } from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -29,28 +28,11 @@ import { AntdConfigProvider } from 
'@/config/antdConfigProvider';
 import { API_CONSUMERS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
-import {
-  pageSearchSchema,
-} from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
+import { pageSearchSchema } from '@/types/schema/pageSearch';
 
 function ConsumersList() {
   const { t } = useTranslation();
-
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'consumers',
-  });
-
-  const consumersQuery = useSuspenseQuery(
-    getConsumerListQueryOptions(pagination)
-  );
-  const { data, isLoading, refetch } = consumersQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = useConsumerList();
 
   const columns = useMemo<ProColumns<APISIXType['RespConsumerItem']>[]>(() => {
     return [
@@ -109,13 +91,7 @@ function ConsumersList() {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/global_rules/add.tsx b/src/routes/global_rules/add.tsx
index ff59f4abb..3f0641f6f 100644
--- a/src/routes/global_rules/add.tsx
+++ b/src/routes/global_rules/add.tsx
@@ -25,11 +25,12 @@ import { nanoid } from 'nanoid';
 import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 
-import { putGlobalRuleReq } from '@/apis/plugins';
+import { putGlobalRuleReq } from '@/apis/global_rules';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartGlobalRules } from 
'@/components/form-slice/FormPartGlobalRules';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
+import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import { APISIX } from '@/types/schema/apisix';
 
@@ -38,7 +39,18 @@ const GlobalRuleAddForm = () => {
   const router = useReactRouter();
 
   const putGlobalRule = useMutation({
-    mutationFn: putGlobalRuleReq,
+    mutationFn: (d: APISIXType['GlobalRulePut']) => putGlobalRuleReq(req, d),
+    async onSuccess(res) {
+      notifications.show({
+        id: 'add-global_rule',
+        message: t('info.add.success', { name: t('globalRules.singular') }),
+        color: 'green',
+      });
+      await router.navigate({
+        to: '/global_rules/detail/$id',
+        params: { id: res.data.value.id },
+      });
+    },
   });
 
   const form = useForm({
@@ -52,22 +64,9 @@ const GlobalRuleAddForm = () => {
     mode: 'onChange',
   });
 
-  const submit = async (data: APISIXType['GlobalRulePut']) => {
-    const res = await putGlobalRule.mutateAsync(data);
-    notifications.show({
-      id: 'add-global_rule',
-      message: t('info.add.success', { name: t('globalRules.singular') }),
-      color: 'green',
-    });
-    await router.navigate({
-      to: '/global_rules/detail/$id',
-      params: { id: res.data.value.id },
-    });
-  };
-
   return (
     <FormProvider {...form}>
-      <form onSubmit={form.handleSubmit(submit)}>
+      <form onSubmit={form.handleSubmit((d) => putGlobalRule.mutateAsync(d))}>
         <FormPartGlobalRules />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/global_rules/detail.$id.tsx 
b/src/routes/global_rules/detail.$id.tsx
index edc5bc0b5..8ea27f920 100644
--- a/src/routes/global_rules/detail.$id.tsx
+++ b/src/routes/global_rules/detail.$id.tsx
@@ -17,7 +17,7 @@
 import { zodResolver } from '@hookform/resolvers/zod';
 import { Button, Group } from '@mantine/core';
 import { notifications } from '@mantine/notifications';
-import { useMutation, useQuery } from '@tanstack/react-query';
+import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
 import {
   createFileRoute,
   useNavigate,
@@ -28,15 +28,16 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getGlobalRuleQueryOptions, putGlobalRuleReq } from '@/apis/plugins';
+import { putGlobalRuleReq } from '@/apis/global_rules';
+import { getGlobalRuleQueryOptions } from '@/apis/hooks';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartGlobalRules } from 
'@/components/form-slice/FormPartGlobalRules';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_GLOBAL_RULES } from '@/config/constant';
-import type { APISIXType } from '@/types/schema/apisix';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 
 type Props = {
   readOnly: boolean;
@@ -46,7 +47,7 @@ const GlobalRuleDetailForm = (props: Props) => {
   const { readOnly, setReadOnly } = props;
   const { t } = useTranslation();
   const { id } = useParams({ from: '/global_rules/detail/$id' });
-  const detailReq = useQuery(getGlobalRuleQueryOptions(id));
+  const detailReq = useSuspenseQuery(getGlobalRuleQueryOptions(id));
 
   const form = useForm({
     resolver: zodResolver(APISIX.GlobalRulePut),
@@ -63,22 +64,21 @@ const GlobalRuleDetailForm = (props: Props) => {
     }
   }, [detailReq.data, form]);
 
-  const putglobalRule = useMutation({
-    mutationFn: putGlobalRuleReq,
+  const putGlobalRule = useMutation({
+    mutationFn: (d: APISIXType['GlobalRulePut']) => putGlobalRuleReq(req, d),
+    async onSuccess() {
+      notifications.show({
+        message: t('info.edit.success', { name: t('globalRules.singular') }),
+        color: 'green',
+      });
+      await detailReq.refetch();
+      setReadOnly(true);
+    },
   });
-  const submit = async (data: APISIXType['GlobalRulePut']) => {
-    await putglobalRule.mutateAsync(data);
-    notifications.show({
-      message: t('info.edit.success', { name: t('globalRules.singular') }),
-      color: 'green',
-    });
-    await detailReq.refetch();
-    setReadOnly(true);
-  };
 
   return (
     <FormProvider {...form}>
-      <form onSubmit={form.handleSubmit(submit)}>
+      <form onSubmit={form.handleSubmit((d) => putGlobalRule.mutateAsync(d))}>
         <FormPartGlobalRules />
         {!readOnly && (
           <Group>
diff --git a/src/routes/global_rules/index.tsx 
b/src/routes/global_rules/index.tsx
index ec5a9c647..248455efa 100644
--- a/src/routes/global_rules/index.tsx
+++ b/src/routes/global_rules/index.tsx
@@ -16,59 +16,36 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { queryOptions, useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
+import { getGlobalRuleListQueryOptions, useGlobalRuleList } from 
'@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
 import { AntdConfigProvider } from '@/config/antdConfigProvider';
 import { API_GLOBAL_RULES } from '@/config/constant';
 import { queryClient } from '@/config/global';
-import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
-import {
-  pageSearchSchema,
-  type PageSearchType,
-} from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
+import { pageSearchSchema } from '@/types/schema/pageSearch';
+
 
-const genGlobalRulesQueryOptions = (props: PageSearchType) => {
-  const { page, pageSize } = props;
-  return queryOptions({
-    queryKey: ['global_rules', page, pageSize],
-    queryFn: () =>
-      req
-        .get<unknown, APISIXType['RespGlobalRuleList']>(API_GLOBAL_RULES, {
-          params: {
-            page,
-            page_size: pageSize,
-          },
-        })
-        .then((v) => v.data),
-  });
-};
 
 function RouteComponent() {
   const { t } = useTranslation();
 
-  // Use the pagination hook
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'global_rules',
-  });
-
-  const globalRulesQuery = useSuspenseQuery(
-    genGlobalRulesQueryOptions(pagination)
+  return (
+    <>
+      <PageHeader title={t('sources.globalRules')} />
+      <GlobalRulesList />
+    </>
   );
-  const { data, isLoading, refetch } = globalRulesQuery;
+}
 
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+function GlobalRulesList() {
+  const { t } = useTranslation();
+  const { data, isLoading, refetch, pagination } = useGlobalRuleList();
 
   const columns = useMemo<
     ProColumns<APISIXType['RespConsumerGroupItem']>[]
@@ -104,46 +81,37 @@ function RouteComponent() {
   }, [t, refetch]);
 
   return (
-    <>
-      <PageHeader title={t('sources.globalRules')} />
-      <AntdConfigProvider>
-        <ProTable
-          columns={columns}
-          dataSource={data?.list || []}
-          rowKey="id"
-          loading={isLoading}
-          search={false}
-          options={false}
-          pagination={{
-            current: pagination.page,
-            pageSize: pagination.pageSize,
-            total: pagination.total,
-            showSizeChanger: true,
-            onChange: handlePageChange,
-          }}
-          cardProps={{ bodyStyle: { padding: 0 } }}
-          toolbar={{
-            menu: {
-              type: 'inline',
-              items: [
-                {
-                  key: 'add',
-                  label: (
-                    <ToAddPageBtn
-                      key="add"
-                      to="/global_rules/add"
-                      label={t('info.add.title', {
-                        name: t('globalRules.singular'),
-                      })}
-                    />
-                  ),
-                },
-              ],
-            },
-          }}
-        />
-      </AntdConfigProvider>
-    </>
+    <AntdConfigProvider>
+      <ProTable
+        columns={columns}
+        dataSource={data.list}
+        rowKey="id"
+        loading={isLoading}
+        search={false}
+        options={false}
+        pagination={pagination}
+        cardProps={{ bodyStyle: { padding: 0 } }}
+        toolbar={{
+          menu: {
+            type: 'inline',
+            items: [
+              {
+                key: 'add',
+                label: (
+                  <ToAddPageBtn
+                    key="add"
+                    to="/global_rules/add"
+                    label={t('info.add.title', {
+                      name: t('globalRules.singular'),
+                    })}
+                  />
+                ),
+              },
+            ],
+          },
+        }}
+      />
+    </AntdConfigProvider>
   );
 }
 
@@ -152,5 +120,5 @@ export const Route = createFileRoute('/global_rules/')({
   validateSearch: pageSearchSchema,
   loaderDeps: ({ search }) => search,
   loader: ({ deps }) =>
-    queryClient.ensureQueryData(genGlobalRulesQueryOptions(deps)),
+    queryClient.ensureQueryData(getGlobalRuleListQueryOptions(deps)),
 });
diff --git a/src/routes/plugin_configs/add.tsx 
b/src/routes/plugin_configs/add.tsx
index dca90c2ea..4f4ea71aa 100644
--- a/src/routes/plugin_configs/add.tsx
+++ b/src/routes/plugin_configs/add.tsx
@@ -27,7 +27,8 @@ import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartPluginConfig } from 
'@/components/form-slice/FormPartPluginConfig';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const PluginConfigAddForm = () => {
@@ -35,7 +36,8 @@ const PluginConfigAddForm = () => {
   const router = useRouter();
 
   const putPluginConfig = useMutation({
-    mutationFn: putPluginConfigReq,
+    mutationFn: (d: APISIXType['PluginConfigPut']) =>
+      putPluginConfigReq(req, d),
     async onSuccess(response) {
       notifications.show({
         message: t('info.add.success', { name: t('pluginConfigs.singular') }),
diff --git a/src/routes/plugin_configs/detail.$id.tsx 
b/src/routes/plugin_configs/detail.$id.tsx
index d882b9f13..431dc2c90 100644
--- a/src/routes/plugin_configs/detail.$id.tsx
+++ b/src/routes/plugin_configs/detail.$id.tsx
@@ -28,17 +28,16 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import {
-  getPluginConfigQueryOptions,
-  putPluginConfigReq,
-} from '@/apis/plugin_configs';
+import { getPluginConfigQueryOptions } from '@/apis/hooks';
+import { putPluginConfigReq } from '@/apis/plugin_configs';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartPluginConfig } from 
'@/components/form-slice/FormPartPluginConfig';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_PLUGIN_CONFIGS } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -56,7 +55,8 @@ const PluginConfigDetailForm = (props: Props) => {
   const initialValue = data.value;
 
   const putPluginConfig = useMutation({
-    mutationFn: putPluginConfigReq,
+    mutationFn: (d: APISIXType['PluginConfigPut']) =>
+      putPluginConfigReq(req, pipeProduce()({ ...d, id })),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('pluginConfigs.singular') }),
@@ -84,11 +84,7 @@ const PluginConfigDetailForm = (props: Props) => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          putPluginConfig.mutateAsync(pipeProduce()({ ...d, id }))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => 
putPluginConfig.mutateAsync(d))}>
         <FormPartPluginConfig />
         {!readOnly && (
           <Group>
diff --git a/src/routes/plugin_configs/index.tsx 
b/src/routes/plugin_configs/index.tsx
index f646a1fdc..bccb89848 100644
--- a/src/routes/plugin_configs/index.tsx
+++ b/src/routes/plugin_configs/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getPluginConfigListQueryOptions } from '@/apis/plugin_configs';
+import { getPluginConfigListQueryOptions, usePluginConfigList } from 
'@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,25 +29,10 @@ import { API_PLUGIN_CONFIGS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 function PluginConfigsList() {
   const { t } = useTranslation();
-
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'plugin_configs',
-  });
-
-  const pluginConfigsQuery = useSuspenseQuery(
-    getPluginConfigListQueryOptions(pagination)
-  );
-  const { data, isLoading, refetch } = pluginConfigsQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = usePluginConfigList();
 
   const columns = useMemo<
     ProColumns<APISIXType['RespPluginConfigItem']>[]
@@ -115,13 +99,7 @@ function PluginConfigsList() {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/protos/add.tsx b/src/routes/protos/add.tsx
index b8d981359..798dac75f 100644
--- a/src/routes/protos/add.tsx
+++ b/src/routes/protos/add.tsx
@@ -28,6 +28,7 @@ import { postProtoReq } from '@/apis/protos';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartProto } from '@/components/form-slice/FormPartProto';
 import PageHeader from '@/components/page/PageHeader';
+import { req } from '@/config/req';
 import type { APISIXType } from '@/types/schema/apisix';
 import { APISIXProtos } from '@/types/schema/apisix/protos';
 
@@ -40,7 +41,14 @@ const ProtoAddForm = () => {
   const router = useReactRouter();
 
   const postProto = useMutation({
-    mutationFn: postProtoReq,
+    mutationFn: (d: APISIXType['ProtoPost']) => postProtoReq(req, d),
+    async onSuccess() {
+      notifications.show({
+        message: t('info.add.success', { name: t('protos.singular') }),
+        color: 'green',
+      });
+      await router.navigate({ to: '/protos' });
+    },
   });
 
   const form = useForm({
@@ -51,19 +59,9 @@ const ProtoAddForm = () => {
     mode: 'onChange',
   });
 
-  const submit = async (data: APISIXType['ProtoPost']) => {
-    await postProto.mutateAsync(data);
-    notifications.show({
-      id: 'add-proto',
-      message: t('info.add.success', { name: t('protos.singular') }),
-      color: 'green',
-    });
-    await router.navigate({ to: '/protos' });
-  };
-
   return (
     <FormProvider {...form}>
-      <form onSubmit={form.handleSubmit(submit)}>
+      <form onSubmit={form.handleSubmit((d) => postProto.mutateAsync(d))}>
         <FormPartProto />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/protos/detail.$id.tsx b/src/routes/protos/detail.$id.tsx
index 883464026..7035a9384 100644
--- a/src/routes/protos/detail.$id.tsx
+++ b/src/routes/protos/detail.$id.tsx
@@ -28,7 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getProtoQueryOptions, putProtoReq } from '@/apis/protos';
+import { getProtoQueryOptions } from '@/apis/hooks';
+import { putProtoReq } from '@/apis/protos';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartProto } from '@/components/form-slice/FormPartProto';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -36,6 +37,7 @@ import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_PROTOS } from '@/config/constant';
+import { req } from '@/config/req';
 import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
@@ -61,7 +63,7 @@ const ProtoDetailForm = ({ id, readOnly, setReadOnly }: 
ProtoFormProps) => {
   });
 
   const putProto = useMutation({
-    mutationFn: putProtoReq,
+    mutationFn: (d: APISIXType['Proto']) => putProtoReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('protos.singular') }),
@@ -85,11 +87,7 @@ const ProtoDetailForm = ({ id, readOnly, setReadOnly }: 
ProtoFormProps) => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          putProto.mutateAsync(pipeProduce()(d))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => putProto.mutateAsync(d))}>
         <FormSectionGeneral />
         <FormPartProto allowUpload={!readOnly} />
         {!readOnly && (
diff --git a/src/routes/protos/index.tsx b/src/routes/protos/index.tsx
index af8436540..275495498 100644
--- a/src/routes/protos/index.tsx
+++ b/src/routes/protos/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getProtoListQueryOptions } from '@/apis/protos';
+import { getProtoListQueryOptions, useProtoList } from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,24 +29,11 @@ import { API_PROTOS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 function RouteComponent() {
   const { t } = useTranslation();
 
-  // Use the pagination hook
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'protos',
-  });
-
-  const protosQuery = useSuspenseQuery(getProtoListQueryOptions(pagination));
-  const { data, isLoading, refetch } = protosQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = useProtoList();
 
   const columns = useMemo<
     ProColumns<APISIXType['RespProtoList']['data']['list'][number]>[]
@@ -93,13 +79,7 @@ function RouteComponent() {
           loading={isLoading}
           search={false}
           options={false}
-          pagination={{
-            current: pagination.page,
-            pageSize: pagination.pageSize,
-            total: pagination.total,
-            showSizeChanger: true,
-            onChange: handlePageChange,
-          }}
+          pagination={pagination}
           cardProps={{ bodyStyle: { padding: 0 } }}
           toolbar={{
             menu: {
diff --git a/src/routes/secrets/add.tsx b/src/routes/secrets/add.tsx
index 83295d917..752b42e9a 100644
--- a/src/routes/secrets/add.tsx
+++ b/src/routes/secrets/add.tsx
@@ -28,7 +28,8 @@ import { FormPartSecret } from 
'@/components/form-slice/FormPartSecret';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import PageHeader from '@/components/page/PageHeader';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const SecretAddForm = () => {
@@ -36,7 +37,8 @@ const SecretAddForm = () => {
   const router = useRouter();
 
   const putSecret = useMutation({
-    mutationFn: putSecretReq,
+    mutationFn: (d: APISIXType['Secret']) =>
+      putSecretReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.add.success', { name: t('secrets.singular') }),
@@ -61,11 +63,7 @@ const SecretAddForm = () => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          putSecret.mutateAsync(pipeProduce()(d))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => putSecret.mutateAsync(d))}>
         <FormSectionGeneral showDate={false} />
         <FormPartSecret />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
diff --git a/src/routes/secrets/detail.$manager.$id.tsx 
b/src/routes/secrets/detail.$manager.$id.tsx
index de6d5db3f..9c2e96599 100644
--- a/src/routes/secrets/detail.$manager.$id.tsx
+++ b/src/routes/secrets/detail.$manager.$id.tsx
@@ -28,7 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getSecretQueryOptions, putSecretReq } from '@/apis/secrets';
+import { getSecretQueryOptions } from '@/apis/hooks';
+import { putSecretReq } from '@/apis/secrets';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartSecret } from '@/components/form-slice/FormPartSecret';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -36,6 +37,7 @@ import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_SECRETS } from '@/config/constant';
+import { req } from '@/config/req';
 import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
@@ -73,7 +75,8 @@ const SecretDetailForm = (props: Props) => {
   }, [secretData, form, isLoading, readOnly]);
 
   const putSecret = useMutation({
-    mutationFn: putSecretReq,
+    mutationFn: (d: APISIXType['Secret']) =>
+      putSecretReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('secrets.singular') }),
@@ -90,11 +93,7 @@ const SecretDetailForm = (props: Props) => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) => {
-          putSecret.mutateAsync(pipeProduce()(d));
-        })}
-      >
+      <form onSubmit={form.handleSubmit((d) => putSecret.mutateAsync(d))}>
         <FormSectionGeneral readOnly />
         <FormPartSecret readOnlyManager />
         {!readOnly && (
diff --git a/src/routes/secrets/index.tsx b/src/routes/secrets/index.tsx
index 18a7417e7..fa9810c34 100644
--- a/src/routes/secrets/index.tsx
+++ b/src/routes/secrets/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getSecretListQueryOptions } from '@/apis/secrets';
+import { getSecretListQueryOptions, useSecretList } from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,23 +29,10 @@ import { API_SECRETS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 function SecretList() {
   const { t } = useTranslation();
-
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'secrets',
-  });
-
-  const secretsQuery = useSuspenseQuery(getSecretListQueryOptions(pagination));
-  const { data, isLoading, refetch } = secretsQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = useSecretList();
 
   const columns = useMemo<
     ProColumns<APISIXType['RespSecretList']['data']['list'][number]>[]
@@ -101,13 +87,7 @@ function SecretList() {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/services/add.tsx b/src/routes/services/add.tsx
index 37b83ad9b..469e380ab 100644
--- a/src/routes/services/add.tsx
+++ b/src/routes/services/add.tsx
@@ -21,12 +21,13 @@ import { createFileRoute, useRouter } from 
'@tanstack/react-router';
 import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 
-import { postServiceReq } from '@/apis/services';
+import { postServiceReq, type ServicePostType } from '@/apis/services';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartService } from '@/components/form-slice/FormPartService';
 import { ServicePostSchema } from 
'@/components/form-slice/FormPartService/schema';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
+import { req } from '@/config/req';
 import { pipeProduce } from '@/utils/producer';
 
 const ServiceAddForm = () => {
@@ -34,7 +35,7 @@ const ServiceAddForm = () => {
   const router = useRouter();
 
   const postService = useMutation({
-    mutationFn: postServiceReq,
+    mutationFn: (d: ServicePostType) => postServiceReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.add.success', { name: t('services.singular') }),
@@ -53,11 +54,7 @@ const ServiceAddForm = () => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          postService.mutateAsync(pipeProduce()(d))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => postService.mutateAsync(d))}>
         <FormPartService />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/services/detail.$id.tsx 
b/src/routes/services/detail.$id.tsx
index 9c613f7a1..bf73a60bc 100644
--- a/src/routes/services/detail.$id.tsx
+++ b/src/routes/services/detail.$id.tsx
@@ -17,7 +17,7 @@
 import { zodResolver } from '@hookform/resolvers/zod';
 import { Button, Group,Skeleton } from '@mantine/core';
 import { notifications } from '@mantine/notifications';
-import { useMutation, useQuery } from '@tanstack/react-query';
+import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
 import {
   createFileRoute,
   useNavigate,
@@ -28,7 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getServiceQueryOptions, putServiceReq } from '@/apis/services';
+import { getServiceQueryOptions } from '@/apis/hooks';
+import { putServiceReq } from '@/apis/services';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartService } from '@/components/form-slice/FormPartService';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -36,7 +37,8 @@ import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_SERVICES } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -49,7 +51,7 @@ const ServiceDetailForm = (props: Props) => {
   const { t } = useTranslation();
   const { id } = useParams({ from: '/services/detail/$id' });
 
-  const serviceQuery = useQuery(getServiceQueryOptions(id));
+  const serviceQuery = useSuspenseQuery(getServiceQueryOptions(id));
   const { data: serviceData, isLoading, refetch } = serviceQuery;
 
   const form = useForm({
@@ -67,7 +69,8 @@ const ServiceDetailForm = (props: Props) => {
   }, [serviceData, form, isLoading]);
 
   const putService = useMutation({
-    mutationFn: putServiceReq,
+    mutationFn: (d: APISIXType['Service']) =>
+      putServiceReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('services.singular') }),
@@ -84,11 +87,7 @@ const ServiceDetailForm = (props: Props) => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) => {
-          putService.mutateAsync(pipeProduce()(d));
-        })}
-      >
+      <form onSubmit={form.handleSubmit((d) => putService.mutateAsync(d))}>
         <FormSectionGeneral />
         <FormPartService />
         {!readOnly && (
diff --git a/src/routes/services/index.tsx b/src/routes/services/index.tsx
index b01b776ed..10fa740b2 100644
--- a/src/routes/services/index.tsx
+++ b/src/routes/services/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getServiceListQueryOptions } from '@/apis/services';
+import { getServiceListQueryOptions, useServiceList } from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn,ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,23 +29,11 @@ import { API_SERVICES } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 const ServiceList = () => {
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'services',
-  });
-
-  const query = useSuspenseQuery(getServiceListQueryOptions(pagination));
-  const { data, isLoading, refetch } = query;
+  const { data, isLoading, refetch, pagination } = useServiceList();
   const { t } = useTranslation();
 
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
-
   const columns = useMemo<ProColumns<APISIXType['RespServiceItem']>[]>(() => {
     return [
       {
@@ -110,13 +97,7 @@ const ServiceList = () => {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/ssls/add.tsx b/src/routes/ssls/add.tsx
index fe84f82fd..356f25010 100644
--- a/src/routes/ssls/add.tsx
+++ b/src/routes/ssls/add.tsx
@@ -21,6 +21,7 @@ import { createFileRoute, useRouter } from 
'@tanstack/react-router';
 import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 
+import { postSSLReq } from '@/apis/ssls';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartSSL } from '@/components/form-slice/FormPartSSL';
 import {
@@ -29,17 +30,14 @@ import {
 } from '@/components/form-slice/FormPartSSL/schema';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
-import { API_SSLS } from '@/config/constant';
 import { req } from '@/config/req';
-import { type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 const SSLAddForm = () => {
   const { t } = useTranslation();
   const router = useRouter();
   const postSSL = useMutation({
-    mutationFn: (data: SSLPostType) =>
-      req.post<unknown, APISIXType['RespSSLDetail']>(API_SSLS, data),
+    mutationFn: (d: SSLPostType) => postSSLReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.add.success', { name: t('ssls.singular') }),
@@ -59,11 +57,7 @@ const SSLAddForm = () => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          postSSL.mutateAsync(pipeProduce()(d))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => postSSL.mutateAsync(d))}>
         <FormPartSSL />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/ssls/detail.$id.tsx b/src/routes/ssls/detail.$id.tsx
index 4c323131b..729585e6c 100644
--- a/src/routes/ssls/detail.$id.tsx
+++ b/src/routes/ssls/detail.$id.tsx
@@ -28,18 +28,21 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import { getSSLDetailQueryOptions, putSSLReq } from '@/apis/ssls';
+import { getSSLQueryOptions } from '@/apis/hooks';
+import { putSSLReq } from '@/apis/ssls';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartSSL } from '@/components/form-slice/FormPartSSL';
 import {
   produceToSSLForm,
   SSLPutSchema,
+  type SSLPutType,
 } from '@/components/form-slice/FormPartSSL/schema';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_SSLS } from '@/config/constant';
+import { req } from '@/config/req';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -54,7 +57,7 @@ const SSLDetailForm = (props: Props & { id: string }) => {
     data: { value: sslData },
     isLoading,
     refetch,
-  } = useSuspenseQuery(getSSLDetailQueryOptions(id));
+  } = useSuspenseQuery(getSSLQueryOptions(id));
 
   const form = useForm({
     resolver: zodResolver(SSLPutSchema),
@@ -64,7 +67,7 @@ const SSLDetailForm = (props: Props & { id: string }) => {
   });
 
   const putSSL = useMutation({
-    mutationFn: putSSLReq,
+    mutationFn: (d: SSLPutType) => putSSLReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('ssls.singular') }),
diff --git a/src/routes/ssls/index.tsx b/src/routes/ssls/index.tsx
index 1b485136a..9bc31f207 100644
--- a/src/routes/ssls/index.tsx
+++ b/src/routes/ssls/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getSSLListQueryOptions } from '@/apis/ssls';
+import { getSSLListQueryOptions, useSSLList } from '@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn, ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,23 +29,10 @@ import { API_SSLS } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 function RouteComponent() {
   const { t } = useTranslation();
-
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'ssls',
-  });
-
-  const sslsQuery = useSuspenseQuery(getSSLListQueryOptions(pagination));
-  const { data, isLoading, refetch } = sslsQuery;
-
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
+  const { data, isLoading, refetch, pagination } = useSSLList();
 
   const columns = useMemo<ProColumns<APISIXType['RespSSLItem']>[]>(() => {
     return [
@@ -113,13 +99,7 @@ function RouteComponent() {
           loading={isLoading}
           search={false}
           options={false}
-          pagination={{
-            current: pagination.page,
-            pageSize: pagination.pageSize,
-            total: pagination.total,
-            showSizeChanger: true,
-            onChange: handlePageChange,
-          }}
+          pagination={pagination}
           cardProps={{ bodyStyle: { padding: 0 } }}
           toolbar={{
             menu: {
diff --git a/src/routes/stream_routes/add.tsx b/src/routes/stream_routes/add.tsx
index 0e986a980..405ad9d01 100644
--- a/src/routes/stream_routes/add.tsx
+++ b/src/routes/stream_routes/add.tsx
@@ -24,9 +24,13 @@ import { useTranslation } from 'react-i18next';
 import { postStreamRouteReq } from '@/apis/stream_routes';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartStreamRoute } from 
'@/components/form-slice/FormPartStreamRoute';
-import { StreamRoutePostSchema } from 
'@/components/form-slice/FormPartStreamRoute/schema';
+import {
+  StreamRoutePostSchema,
+  type StreamRoutePostType,
+} from '@/components/form-slice/FormPartStreamRoute/schema';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
 import PageHeader from '@/components/page/PageHeader';
+import { req } from '@/config/req';
 import { pipeProduce } from '@/utils/producer';
 
 const StreamRouteAddForm = () => {
@@ -34,7 +38,8 @@ const StreamRouteAddForm = () => {
   const router = useRouter();
 
   const postStreamRoute = useMutation({
-    mutationFn: postStreamRouteReq,
+    mutationFn: (d: StreamRoutePostType) =>
+      postStreamRouteReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.add.success', { name: t('streamRoutes.singular') }),
@@ -53,11 +58,7 @@ const StreamRouteAddForm = () => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) =>
-          postStreamRoute.mutateAsync(pipeProduce()(d))
-        )}
-      >
+      <form onSubmit={form.handleSubmit((d) => 
postStreamRoute.mutateAsync(d))}>
         <FormPartStreamRoute />
         <FormSubmitBtn>{t('form.btn.add')}</FormSubmitBtn>
       </form>
diff --git a/src/routes/stream_routes/detail.$id.tsx 
b/src/routes/stream_routes/detail.$id.tsx
index 4b634851e..cb1e8d178 100644
--- a/src/routes/stream_routes/detail.$id.tsx
+++ b/src/routes/stream_routes/detail.$id.tsx
@@ -28,10 +28,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import { useBoolean } from 'react-use';
 
-import {
-  getStreamRouteQueryOptions,
-  putStreamRouteReq,
-} from '@/apis/stream_routes';
+import { getStreamRouteQueryOptions } from '@/apis/hooks';
+import { putStreamRouteReq } from '@/apis/stream_routes';
 import { FormSubmitBtn } from '@/components/form/Btn';
 import { FormPartStreamRoute } from 
'@/components/form-slice/FormPartStreamRoute';
 import { FormTOCBox } from '@/components/form-slice/FormSection';
@@ -39,7 +37,8 @@ import { FormSectionGeneral } from 
'@/components/form-slice/FormSectionGeneral';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { API_STREAM_ROUTES } from '@/config/constant';
-import { APISIX } from '@/types/schema/apisix';
+import { req } from '@/config/req';
+import { APISIX, type APISIXType } from '@/types/schema/apisix';
 import { pipeProduce } from '@/utils/producer';
 
 type Props = {
@@ -70,7 +69,8 @@ const StreamRouteDetailForm = (props: Props) => {
   }, [streamRouteData, form, isLoading]);
 
   const putStreamRoute = useMutation({
-    mutationFn: putStreamRouteReq,
+    mutationFn: (d: APISIXType['StreamRoute']) =>
+      putStreamRouteReq(req, pipeProduce()(d)),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('streamRoutes.singular') }),
@@ -87,11 +87,7 @@ const StreamRouteDetailForm = (props: Props) => {
 
   return (
     <FormProvider {...form}>
-      <form
-        onSubmit={form.handleSubmit((d) => {
-          putStreamRoute.mutateAsync(pipeProduce()(d));
-        })}
-      >
+      <form onSubmit={form.handleSubmit((d) => putStreamRoute.mutateAsync(d))}>
         <FormSectionGeneral />
         <FormPartStreamRoute />
         {!readOnly && (
diff --git a/src/routes/stream_routes/index.tsx 
b/src/routes/stream_routes/index.tsx
index b7b5dd5b2..6676af7b3 100644
--- a/src/routes/stream_routes/index.tsx
+++ b/src/routes/stream_routes/index.tsx
@@ -16,12 +16,11 @@
  */
 import type { ProColumns } from '@ant-design/pro-components';
 import { ProTable } from '@ant-design/pro-components';
-import { useSuspenseQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { getStreamRouteListQueryOptions } from '@/apis/stream_routes';
+import { getStreamRouteListQueryOptions, useStreamRouteList } from 
'@/apis/hooks';
 import { DeleteResourceBtn } from '@/components/page/DeleteResourceBtn';
 import PageHeader from '@/components/page/PageHeader';
 import { ToAddPageBtn,ToDetailPageBtn } from '@/components/page/ToAddPageBtn';
@@ -30,23 +29,11 @@ import { API_STREAM_ROUTES } from '@/config/constant';
 import { queryClient } from '@/config/global';
 import type { APISIXType } from '@/types/schema/apisix';
 import { pageSearchSchema } from '@/types/schema/pageSearch';
-import { usePagination } from '@/utils/usePagination';
 
 const StreamRouteList = () => {
-  const { pagination, handlePageChange, updateTotal } = usePagination({
-    queryKey: 'stream_routes',
-  });
-
-  const query = useSuspenseQuery(getStreamRouteListQueryOptions(pagination));
-  const { data, isLoading, refetch } = query;
+  const { data, isLoading, refetch, pagination } = useStreamRouteList();
   const { t } = useTranslation();
 
-  useEffect(() => {
-    if (data?.total) {
-      updateTotal(data.total);
-    }
-  }, [data?.total, updateTotal]);
-
   const columns = useMemo<
     ProColumns<APISIXType['RespStreamRouteItem']>[]
   >(() => {
@@ -101,13 +88,7 @@ const StreamRouteList = () => {
         loading={isLoading}
         search={false}
         options={false}
-        pagination={{
-          current: pagination.page,
-          pageSize: pagination.pageSize,
-          total: pagination.total,
-          showSizeChanger: true,
-          onChange: handlePageChange,
-        }}
+        pagination={pagination}
         cardProps={{ bodyStyle: { padding: 0 } }}
         toolbar={{
           menu: {
diff --git a/src/routes/upstreams/add.tsx b/src/routes/upstreams/add.tsx
index 244504066..a080b815e 100644
--- a/src/routes/upstreams/add.tsx
+++ b/src/routes/upstreams/add.tsx
@@ -41,7 +41,7 @@ const UpstreamAddForm = () => {
   const { t } = useTranslation();
   const router = useRouter();
   const postUpstream = useMutation({
-    mutationFn: (data: PostUpstreamType) => postUpstreamReq(req, data),
+    mutationFn: (d: PostUpstreamType) => postUpstreamReq(req, d),
     async onSuccess(data) {
       notifications.show({
         message: t('info.add.success', { name: t('upstreams.singular') }),
diff --git a/src/routes/upstreams/detail.$id.tsx 
b/src/routes/upstreams/detail.$id.tsx
index 7d8e539f8..441826d21 100644
--- a/src/routes/upstreams/detail.$id.tsx
+++ b/src/routes/upstreams/detail.$id.tsx
@@ -76,7 +76,7 @@ const UpstreamDetailForm = (
   });
 
   const putUpstream = useMutation({
-    mutationFn: (data: APISIXType['Upstream']) => putUpstreamReq(req, data),
+    mutationFn: (d: APISIXType['Upstream']) => putUpstreamReq(req, d),
     async onSuccess() {
       notifications.show({
         message: t('info.edit.success', { name: t('upstreams.singular') }),
diff --git a/src/types/schema/pageSearch.ts b/src/types/schema/pageSearch.ts
index dc2f25b70..fb663566b 100644
--- a/src/types/schema/pageSearch.ts
+++ b/src/types/schema/pageSearch.ts
@@ -16,39 +16,22 @@
  */
 import { z } from 'zod';
 
-/**
- * To deprecate pageSize without modifying existing code, use preprocessing.
- */
-export const pageSearchSchema = z.preprocess(
-  (data) => {
-    // If pageSize is provided but page_size isn't, use pageSize value for 
page_size
-    const inputData = data as Record<string, unknown>;
-    if (inputData?.pageSize && inputData?.page_size === undefined) {
-      return { ...inputData, page_size: inputData.pageSize };
-    }
-    return data;
-  },
-  z
-    .object({
-      page: z
-        .union([z.string(), z.number()])
-        .optional()
-        .default(1)
-        .transform((val) => (val ? Number(val) : 1)),
-      pageSize: z
-        .union([z.string(), z.number()])
-        .optional()
-        .default(10)
-        .transform((val) => (val ? Number(val) : 10)),
-      page_size: z
-        .union([z.string(), z.number()])
-        .optional()
-        .default(10)
-        .transform((val) => (val ? Number(val) : 10)),
-      name: z.string().optional(),
-      label: z.string().optional(),
-    })
-    .passthrough()
-);
+
+export const pageSearchSchema = z
+  .object({
+    page: z
+      .union([z.string(), z.number()])
+      .optional()
+      .default(1)
+      .transform((val) => (val ? Number(val) : 1)),
+    page_size: z
+      .union([z.string(), z.number()])
+      .optional()
+      .default(10)
+      .transform((val) => (val ? Number(val) : 10)),
+    name: z.string().optional(),
+    label: z.string().optional(),
+  })
+  .passthrough();
 
 export type PageSearchType = z.infer<typeof pageSearchSchema>;
diff --git a/src/utils/usePagination.ts b/src/utils/usePagination.ts
deleted file mode 100644
index d30bfd029..000000000
--- a/src/utils/usePagination.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { useRouter } from '@tanstack/react-router';
-import { useCallback, useEffect,useState } from 'react';
-
-import { queryClient } from '@/config/global';
-import type { PageSearchType } from '@/types/schema/pageSearch';
-
-type PaginationState = PageSearchType & {
-  total: number;
-};
-
-interface UsePaginationOptions {
-  initialPage?: number;
-  initialPageSize?: number;
-  initialTotal?: number;
-  queryKey: string;
-  /** If true, updates URL with pagination state */
-  syncWithUrl?: boolean;
-}
-
-export interface UsePaginationResult {
-  pagination: PaginationState;
-  setPagination: React.Dispatch<React.SetStateAction<PaginationState>>;
-  handlePageChange: (page: number, pageSize: number) => void;
-  createQueryKey: (page: number, pageSize: number) => (string | number)[];
-  refreshData: () => void;
-  updateTotal: (total: number) => void;
-}
-
-/**
- * Custom hook for managing pagination state and related operations
- *
- * @param options - Configuration options for pagination
- * @returns Pagination state and handlers
- */
-export function usePagination(
-  options: UsePaginationOptions
-): UsePaginationResult {
-  const {
-    initialPage = 1,
-    initialPageSize = 10,
-    initialTotal = 0,
-    queryKey,
-    syncWithUrl = true,
-  } = options;
-
-  // Get router for URL integration
-  const router = useRouter();
-  const routeSearch = router.state.location.search;
-
-  // Initialize pagination from URL if available
-  const urlPage =
-    syncWithUrl && routeSearch.page
-      ? Number(routeSearch.page) || initialPage
-      : initialPage;
-  const urlPageSize =
-    syncWithUrl && routeSearch.pageSize
-      ? Number(routeSearch.pageSize) || initialPageSize
-      : initialPageSize;
-
-  const [pagination, setPagination] = useState<PaginationState>({
-    page: urlPage,
-    page_size: urlPageSize,
-    pageSize: urlPageSize,
-    total: initialTotal,
-  });
-
-  // Sync URL with state when URL params change
-  useEffect(() => {
-    if (syncWithUrl) {
-      const urlPage = routeSearch.page ? Number(routeSearch.page) || 1 : null;
-      const urlPageSize = routeSearch.pageSize
-        ? Number(routeSearch.pageSize) || 10
-        : null;
-
-      if (
-        (urlPage !== null && urlPage !== pagination.page) ||
-        (urlPageSize !== null && urlPageSize !== pagination.pageSize)
-      ) {
-        setPagination((prev) => ({
-          ...prev,
-          current: urlPage || prev.page,
-          pageSize: urlPageSize || prev.pageSize,
-        }));
-      }
-    }
-  }, [routeSearch, syncWithUrl, pagination]);
-
-  /**
-   * Creates a query key array for React Query based on the given page and 
pageSize
-   */
-  const createQueryKey = useCallback(
-    (page: number, pageSize: number): (string | number)[] => [
-      queryKey,
-      page,
-      pageSize,
-    ],
-    [queryKey]
-  );
-
-  /**
-   * Handles page and page size changes
-   */
-  const handlePageChange = useCallback(
-    (page: number, pageSize: number) => {
-      setPagination((prev) => ({
-        ...prev,
-        current: page,
-        pageSize: pageSize,
-      }));
-
-      // Update URL with new pagination state if enabled
-      if (syncWithUrl) {
-        router.navigate({
-          search: (prev) => {
-            // Keep all existing search params
-            const newSearch = { ...prev };
-            newSearch.page = page;
-            newSearch.pageSize = pageSize;
-            return newSearch as never;
-          },
-          replace: true,
-        });
-      }
-
-      // Invalidate the query to trigger a refetch with new pagination
-      queryClient.invalidateQueries({
-        queryKey: createQueryKey(page, pageSize),
-      });
-    },
-    [createQueryKey, router, syncWithUrl]
-  );
-
-  /**
-   * Updates pagination total when data changes
-   */
-  const updateTotal = useCallback((total: number) => {
-    setPagination((prev) => ({ ...prev, total }));
-  }, []);
-
-  /**
-   * Refreshes current page data
-   */
-  const refreshData = useCallback(() => {
-    const { page, pageSize } = pagination;
-    queryClient.invalidateQueries({
-      queryKey: createQueryKey(page, pageSize),
-    });
-  }, [createQueryKey, pagination]);
-
-  return {
-    pagination,
-    setPagination,
-    handlePageChange,
-    createQueryKey,
-    refreshData,
-    updateTotal,
-  };
-}
diff --git a/src/utils/useSearchParams.ts b/src/utils/useSearchParams.ts
index 35fc8315b..a9c3b3cfa 100644
--- a/src/utils/useSearchParams.ts
+++ b/src/utils/useSearchParams.ts
@@ -22,21 +22,22 @@ import {
 } from '@tanstack/react-router';
 import { useCallback } from 'react';
 
-import type { PageSearchType } from '@/types/schema/pageSearch';
 
-type RouteTreeIds = RouteIds<RegisteredRouter['routeTree']>;
 
-export const useSearchParams = <T extends RouteTreeIds>(routeId: T) => {
+export type RouteTreeIds = RouteIds<RegisteredRouter['routeTree']>;
+
+export const useSearchParams = <T extends RouteTreeIds, P extends object>(
+  routeId: T
+) => {
   const { useSearch } = getRouteApi<T>(routeId);
   const navigate = useNavigate();
-  const params = useSearch();
-  type Params = typeof params;
+  const params = useSearch() as P;
 
   const setParams = useCallback(
-    (props: Partial<Params>) => {
+    (props: Partial<P>) => {
       return navigate({
         to: '.',
-        search: (prev: object) => ({ ...prev, ...props }),
+        search: (prev) => ({ ...prev, ...props }),
       });
     },
     [navigate]
@@ -46,9 +47,10 @@ export const useSearchParams = <T extends 
RouteTreeIds>(routeId: T) => {
     [navigate]
   );
 
-  return { params: params as PageSearchType, setParams, resetParams } as const;
+  return { params, setParams, resetParams } as const;
 };
 
-export type UseSearchParams<T extends RouteTreeIds> = ReturnType<
-  typeof useSearchParams<T>
->;
+export type UseSearchParams<
+  T extends RouteTreeIds,
+  P extends object
+> = ReturnType<typeof useSearchParams<T, P>>;
diff --git a/src/utils/useTablePagination.ts b/src/utils/useTablePagination.ts
index 602a510be..a1e376a7c 100644
--- a/src/utils/useTablePagination.ts
+++ b/src/utils/useTablePagination.ts
@@ -20,18 +20,23 @@ import { useCallback, useMemo } from 'react';
 
 import type { FileRoutesByTo } from '@/routeTree.gen';
 import type { APISIXListResponse } from '@/types/schema/apisix/type';
-import { pageSearchSchema } from '@/types/schema/pageSearch';
+import {
+  pageSearchSchema,
+  type PageSearchType,
+} from '@/types/schema/pageSearch';
 
 import type { UseSearchParams } from './useSearchParams';
 
-type ListPageKeys = `${keyof FilterKeys<FileRoutesByTo, 's'>}/`;
-type Props<T> = {
+export type ListPageKeys = `${keyof FilterKeys<FileRoutesByTo, 's'>}/`;
+type Props<T, P extends PageSearchType> = {
   data: APISIXListResponse<T>;
   /** if params is from useSearchParams, refetch is not needed */
   refetch?: () => void;
-} & Pick<UseSearchParams<ListPageKeys>, 'params' | 'setParams'>;
+} & Pick<UseSearchParams<ListPageKeys, P>, 'params' | 'setParams'>;
 
-export const useTablePagination = <T>(props: Props<T>) => {
+export const useTablePagination = <T, P extends PageSearchType>(
+  props: Props<T, P>
+) => {
   const { data, refetch, setParams } = props;
   const params = useMemo(
     () => pageSearchSchema.parse(props.params),
@@ -40,8 +45,8 @@ export const useTablePagination = <T>(props: Props<T>) => {
   const { page, page_size } = params;
 
   const onChange: TablePaginationConfig['onChange'] = useCallback(
-    (page: number, pageSize: number) => {
-      setParams({ page, page_size: pageSize });
+    (page: number, page_size: number) => {
+      setParams({ page, page_size } as P);
       refetch?.();
     },
     [refetch, setParams]

Reply via email to