如何修改GraphQL返回的数据结构?(Gatsby+Strapi+Apollo场景)
问题描述
背景
站点基于Gatsby构建,搭配提供GraphQL查询的Strapi,以及用于数据获取的Apollo Client。
当前实现
刚在应用中集成Strapi,相关代码如下:getArticles.ts
import { gql } from "@apollo/client"; export const GET_ARTICLES = gql` query Article { articles { data { id attributes { title description date } } } } `;
组件中使用:
const { loading, error, data } = useQuery(GET_ARTICLES);
打印data得到的结构:
{ "data": { "articles": { "data": [ { "id": "1", "attributes": { "title": "Article 1", "description": "This is the first article", "date": "2023-06-23" } }, { "id": "2", "attributes": { "title": "Article 2", "description": "This is the second article", "date": "2023-06-24" } } ] } } }
预期效果
期望data直接返回如下数组格式:
[ { "id": "1", "attributes": { "title": "Article 1", "description": "This is the first article", "date": "2023-06-23" } }, { "id": "2", "attributes": { "title": "Article 2", "description": "This is the second article", "date": "2023-06-24" } } ]
疑问
不清楚该修改哪一层实现此效果?虽可在组件中用const myData = data?.articles?.data提取,但不想每次查询都重复写这段代码,希望直接获取目标格式。附gatsby-configs.ts相关配置:
{ resolve: "gatsby-source-strapi", options: { apiURL: process.env.STRAPI_API_URL || "http://127.0.0.1:1337", accessToken: process.env.STRAPI_TOKEN, collectionTypes: [ { singularName: "article", queryParams: { "preview" : "live", populate: { cover: "*", blocks: { populate: "*", }, }, }, } ] }, },
解决方案
方案1:自定义Apollo useQuery Hook
封装通用Hook自动处理数据提取,避免重复代码:
import { useQuery as useApolloQuery, DocumentNode, QueryHookOptions } from '@apollo/client'; export const useQuery = <T = any>( query: DocumentNode, options?: QueryHookOptions<T> ) => { const { loading, error, data } = useApolloQuery<T>(query, options); // 自动提取集合对应的data数组 const transformedData = data ? Object.values(data)[0]?.data : undefined; return { loading, error, data: transformedData }; };
组件中替换原useQuery使用:
import { useQuery } from './path-to-your-custom-hook'; import { GET_ARTICLES } from './getArticles'; const { loading, error, data } = useQuery(GET_ARTICLES); // 此时data直接是目标数组格式
方案2:Apollo客户端添加响应拦截器
在Apollo Client初始化时添加拦截器,统一处理响应结构:
import { ApolloClient, InMemoryCache, createHttpLink, ApolloLink } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: `${process.env.STRAPI_API_URL}/graphql`, }); // 响应拦截:自动提取集合的data字段 const responseLink = new ApolloLink((operation, forward) => { return forward(operation).map((response) => { if (response.data) { Object.keys(response.data).forEach((key) => { if (response.data[key]?.data) { response.data[key] = response.data[key].data; } }); } return response; }); }); const authLink = setContext((_, { headers }) => { const token = process.env.STRAPI_TOKEN; return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, }; }); const client = new ApolloClient({ link: authLink.concat(responseLink.concat(httpLink)), cache: new InMemoryCache(), }); export default client;
之后使用原useQuery时,data.articles直接就是目标数组;若想让返回的data直接为数组,可在拦截器中根据查询名称进一步判断处理。
方案3:使用Gatsby原生GraphQL查询(推荐静态场景)
既然配置了gatsby-source-strapi,直接用Gatsby的静态查询或页面查询,Gatsby会自动将Strapi数据转换为扁平节点结构:
query GetArticles { allStrapiArticle { nodes { id title description date } } }
此时data.allStrapiArticle.nodes就是目标数组,且无需手动处理attributes嵌套,更符合Gatsby静态生成的最佳实践。
内容的提问来源于stack exchange,提问作者CCCC
相关产品推荐
相关产品推荐

