从GraphQL响应中清除不需要的字段

时间:2017-11-09 21:24:23

标签: javascript ecmascript-6 graphql

我有一个我的GraphQL客户端请求的对象。

这是一个相当简单的对象:

type Element {
    content: [ElementContent]
    elementId: String
    name: String
    notes: String
    type: String
    createdAt: String
    updatedAt: String
  }

使用特殊类型ElementContent,它很小,看起来像这样:

  type ElementContent {
    content: String
    locale: String
  }

现在,当我在客户端查询时,顶级对象和低级对象都有其他属性(如果我尝试按原样克隆主体,则会干扰更新对象);

值得注意的是,GraphQL似乎在父对象中提供了__typename属性,并且在子对象中,它们也具有typename和Symbol(id)属性。

enter image description here

我想将此对象复制到状态,更新状态,然后克隆状态并将其发送到我的update突变。但是,由于GraphQL本身提供的未知属性,我遇到了障碍。

我尝试过:

delete element.__typename效果很好,但是我还需要遍历子项(动态数组对象),并且可能还必须删除这些属性。

我不确定在这个等式中我是否遗漏了某些东西,或者我应该在代码中挣扎并循环+删除(我最初尝试执行forEach循环时收到错误)。对于我正在尝试做的事情,是否有更好的策略?或者我是在正确的道路上,只需要一些好的循环代码来清理不需要的属性?

4 个答案:

答案 0 :(得分:2)

有三种方法

第一种方式

像这样更新客户端参数,它将忽略graphql中不需要的字段。

apollo.create({
  link: http,
  cache: new InMemoryCache({
    addTypename: false
  })
});

第二种方式

通过使用omit-deep软件包并将其用作中间件

const cleanTypeName = new ApolloLink((operation, forward) => {
  if (operation.variables) {

    operation.variables = omitDeep(operation.variables,'__typename')
  }
  return forward(operation).map((data) => {
    return data;
  });
});

第三种方式

创建自定义中间件并注入到阿波罗中

const cleanTypeName = new ApolloLink((operation, forward) => {
  if (operation.variables) {
    const omitTypename = (key, value) => (key === '__typename' ? undefined : value);
    operation.variables = JSON.parse(JSON.stringify(operation.variables), omitTypename);
  }
  return forward(operation).map((data) => {
    return data;
  });
});

并注入中间件

const httpLinkWithErrorHandling = ApolloLink.from([
  cleanTypeName,
  retry,
  error,
  http,
]);

首选方法为第三种方法,因为它没有任何第三种依赖关系,也没有缓存性能问题

答案 1 :(得分:2)

如果要从GraphQL响应中清除__typename(从根目录及其子级开始),可以使用graphql-anywhere软件包。

类似: const wipedData = filter(inputFragment, rcvData);

  • inputFragment是定义字段的片段(您可以查看详细信息here
  • rcvData是从GraphQL查询接收的数据

通过使用filter函数,wipedData仅包含您需要作为突变输入传递的必填字段。

答案 2 :(得分:0)

这就是我所做的,也支持文件上传。它是我在Github线程上找到的多个建议的合并:Feature idea: Automatically remove __typename from mutations

import { parse, stringify } from 'flatted';

const cleanTypename = new ApolloLink((operation, forward) => {
    const omitTypename = (key, value) => (key === '__typename' ? undefined : value);

    if ((operation.variables && !operation.getContext().hasUpload)) {
        operation.variables = parse(stringify(operation.variables), omitTypename);
    }

    return forward(operation);
});

简化我的client.tsx文件的其余部分:

import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient } from 'apollo-client';
import { setContext } from 'apollo-link-context';
import { ApolloLink } from 'apollo-link';

const authLink = setContext((_, { headers }) => {
    const token = localStorage.getItem(AUTH_TOKEN);
    return {
        headers: {
        ...headers,
        authorization: token ? `Bearer ${ token }` : '',
        },
    };
});



const httpLink = ApolloLink.from([
    cleanTypename,
    authLink.concat(upLoadLink),
]);

const client = new ApolloClient({
    link: httpLink,
    cache,
});

export default client;

现在,当我调用类型为上载的突变时,只需将上下文hasUpload设置为true,如下所示:

UpdateStation({variables: { input: station }, context: {hasUpload: true }}).then()

答案 3 :(得分:0)

对于那些正在寻找 TypeScript 解决方案的人:

import cloneDeepWith from "lodash/cloneDeepWith";

export const omitTypenameDeep = (
  variables: Record<string, unknown>
): Record<string, unknown> =>
  cloneDeepWith(variables, (value) => {
    if (value && value.__typename) {
      const { __typename, ...valWithoutTypename } = value;
      return valWithoutTypename;
    }

    return undefined;
  });
const removeTypename = new ApolloLink((operation, forward) => {
  const newOperation = operation;
  newOperation.variables = omitTypenameDeep(newOperation.variables);
  return forward(newOperation);
});

// ...

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: ApolloLink.from([removeTypename, httpLink]),
});