首页 > 解决方案 > Can't custom value of graphql enum

问题描述

I have looked this question: How to use or resolve enum types with graphql-tools?

And, this doc: https://www.apollographql.com/docs/graphql-tools/scalars/#internal-values

Now, I want to custom the value of graphql enum.

typeDefs.ts:

import { gql } from 'apollo-server';

export const typeDefs = gql`
  enum Device {
    UNKNOWN
    DESKTOP
    HIGH_END_MOBILE
    TABLET
    CONNECTED_TV
  }

  type CampaignPerformanceReport {
    campaignNme: String!
    campaignId: ID!
    device: Device
  }

  type Query {
    campaignPerformanceReports: [CampaignPerformanceReport]!
  }
`;

resolvers.ts:

import { IResolvers } from 'graphql-tools';
import { IAppContext } from './appContext';

export const resolvers: IResolvers = {
  Device: {
    UNKNOWN: 'Other',
    DESKTOP: 'Computers',
    HIGH_END_MOBILE: 'Mobile devices with full browsers',
    TABLET: 'Tablets with full browsers',
    CONNECTED_TV: 'Devices streaming video content to TV screens',
  },
  Query: {
    async campaignPerformanceReports(_, __, { db }: IAppContext) {
      return db.campaignPerformanceReports;
    },
  },
};

As you can see, I custom the value of Device enum in the resolver.

db.ts: a fake db with datas

enum Device {
  UNKNOWN = 'Other',
  DESKTOP = 'Computers',
  HIGH_END_MOBILE = 'Mobile devices with full browsers',
  TABLET = 'Tablets with full browsers',
  CONNECTED_TV = 'Devices streaming video content to TV screens',
}

export const db = {
  campaignPerformanceReports: [
    {
      campaignId: 1,
      campaignNme: 'test',
      device: Device.DESKTOP,
    },
  ],
};

I also made an integration test for this:

 test.only('should query campaign performance reports correctly with executable graphql schema', async () => {
      const schema = makeExecutableSchema({ typeDefs, resolvers });
      console.log(printSchema(schema));
      const server: ApolloServerBase = new ApolloServer({ schema, context: { db } });
      const { query }: ApolloServerTestClient = createTestClient(server);
      const res: GraphQLResponse = await query({ query: Q.campaignPerformanceReports });
      expect(res).toMatchInlineSnapshot(`
        Object {
          "data": Object {
            "campaignPerformanceReports": Array [
              Object {
                "campaignId": "1",
                "campaignNme": "test",
                "device": "DESKTOP",
              },
            ],
          },
          "errors": undefined,
          "extensions": undefined,
          "http": Object {
            "headers": Headers {
              Symbol(map): Object {},
            },
          },
        }
      `);
    });

As you can see, the result of snapshot testing. The value of device field is still "DESKTOP", I expected the value should be "Computers"

Dependencies version:

"apollo-server": "^2.9.3",
"apollo-server-express": "^2.9.3",
"graphql": "^14.5.4",

The minimal repo: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/custom-scalar-and-enum

标签: graphqlgraphql-jsapollo-server

解决方案


The internal values you specify for a GraphQL enum are just that -- internal. This is stated in the documentation:

These don't change the public API at all, but they do allow you to use that value instead of the schema value in your resolvers

If you map the enum value DESKTOP to the internal value Computers, only the behavior of your resolvers will be affected. Specifically:

  • If a field takes an argument of the type Device and the argument is passed the value DESKTOP, the value actually passed to the resolver function will be Computers.
  • If a field itself has the type device and we want to return DESKTOP, inside our resolver, we will need to return Computers instead.

Take for example a schema that looks like this:

type Query {
  someQuery(device: Device!): Device!
}

If you don't specify internal values, our resolver works like this:

function (parent, args) {
  console.log(args.device) // "DESKTOP"
  return 'DESKTOP'
}

If you do specify internal values, the resolver looks like this:

function (parent, args) {
  console.log(args.device) // "Computers"
  return 'Computers'
}

The resolver is the only thing impacted by providing internal values for each enum value. What doesn't change:

  • How the enum value is serialized in the response. Enum values are always serialized as strings of the enum value name.
  • How the enum value is written as a literal inside a document. For example, if querying the above same field, we would always write: { someQuery(device: DESKTOP) }
  • How the enum value is provided as a variable. A variable of the type Device would always be written as "DESKTOP".

NOTE: While the question pertains specifically to Apollo Server, the above applies to vanilla GraphQL.js as well. For example, this enum

const DeviceEnum = new GraphQLEnumType({
  name: 'Device',
  values: {
    UNKNOWN: { value: 'Other' },
    DESKTOP: { value: 'Computers' },
    HIGH_END_MOBILE: { value: 'Mobile devices with full browsers' },
    TABLET: { value: 'Tablets with full browsers' },
    CONNECTED_TV: { value: 'Devices streaming video content to TV screens' },
  }
})

will still behave as described above.


推荐阅读