Leveraging introspection queries to fetch enum constants defined in a back-end (BE) Graph API to use in corresponding front-end (FE) interfaces.
For smaller & monolithic projects, it's easy to have GraphQL types defined in a shared folder that both can reference. But the FE & BE for a project I'm currently working are isolated microservices & live in separate repositories.
Enums live at the service layer as they are tightly coupled to the types they define. But there are instances where the client also needs to know these enums (like a dropdown of discrete values).
There are many ways to go about getting these to the FE:
- You could copy&paste them from the BE to the FE codebase, but that's bad practice. 🤔
- A simple endpoint could provide these enums, but that requires effort to build/maintain. 🤔
- A shared library of types/enums could be depended on by both repositories, but that's a pain to keep updated. 🤔
- Or you could use GraphQL's built-in
IntrospectionQuery
to determine them, which I recently used to refactor away from the first^ method in a FE application. ✅
For the sake of metaphor I'm using the salvage/treasure example, but that's not to say the BE is dead or broken! It's very much the opposite, I just like the metaphor so sticking with it. 😁
Defining enums on BE 🔩
I won't go deep into this, but let's assume you have a graph service with a schema that has an enum defined like so:
enum TreasureTypeEnum {
GOLD_DOUBLOON,
SILVER_COIN,
EMERALD_RING
}
Fetching on FE 🤿
An introspection query is magical; it primarily returns the queries an API supports, but diving further it can provide supported enum values. Simply create the generic enum value introspection query, which takes the enum name as a param:
import gql from 'graphql-tag';
const EnumValuesIntrospectionQuery = gql`
query ($name: String!) {
__type(name: $name) {
enumValues {
name
}
}
}
`;
Then use it to fetch values for the type:
import { useQuery } from 'react-apollo';
const {
loading, error, data
} = useQuery(EnumValuesIntrospectionQuery, {
variables: {
name: "TreasureTypeEnum"
}
});
const { __type: { enumValues } } = data;
const formattedEnumValues = enumValues.map(({ name }) => name);
console.log(formattedEnumValues);
// > ["GOLD_DOUBLOON", "SILVER_COIN", "EMERALD_RING"]
For a prettier display (e.g. labels), a simple formatter that replaces underscores for spaces & text-transform: capitalize
via CSS will do the trick.
Now we can render our salvaged treasure in a dropdown so the crew can select what they'd like in a HTML form. 🏴☠️
Thanks for reading! Anyone have a different/better approach to keep enums on FE consistent with BE?