Skip to content

Instantly share code, notes, and snippets.

@StevenACoffman
Created August 22, 2022 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StevenACoffman/cc6bcd6ce168d4689b9d84414845b36e to your computer and use it in GitHub Desktop.
Save StevenACoffman/cc6bcd6ce168d4689b9d84414845b36e to your computer and use it in GitHub Desktop.
Differences in the Type System in GraphQL vs Go

Something worth pointing out is that the GraphQL and Go type systems have some pretty glaring differences, and, at least for me, it can be hard to know which mental model to reason with when working with Go code generated from a GraphQL schema. For instance, GraphQL requires explicitly stating that a type implements an interface in order for it to be used as that interface type.

Here's a list of type system differences:

  • Unions exist in GraphQL, but not Go, so they are somewhat mocked through Go interfaces
    • The Is{UnionName}() signpost functions leverage the Go compiler to help alleviate some of the confusion here
    • Without the signposts, a programmer implementing a query resolver would need likely need to jump to the GraphQL schema to figure out what types are included in the union
  • Interfaces in GraphQL differ from Go interfaces in two key ways
    • A GraphQL interface definition includes only fields, where Go interfaces contain methods
      • Getter functions are generated to preserve the fields defined in the GraphQL interface
    • GraphQL types must explicitly declare that they're implementing an interface to be used as that interface type, where Go does this implicitly, i.e. if a type has the same methods that an interface defines, it can be used as that interface type
      • The signpost functions prevent Go types from inadvertently implementing GraphQL interfaces
        • As an example, imagine two different GraphQL interfaces that only contain ID: ID!
  • GraphQL makes you use special input types if you need structured input to a query or mutation
    • input types can't be used as returns, solely as inputs
    • Go's type system has no analog, and no great way to enforce this
  • GraphQL types have the notion of "required" fields, e.g. Int!, where Go only has pointers
    • For primitives, it is somewhat convenient to just use the type directly for required fields, and pointers for optional fields
    • For structures, this becomes a hard decision, since using a structure type directly in Go will force copies to be created
      • The compromise here is that structure fields are always generated as pointer in Go, and, if they're marked as required in the GraphQL schema, the resolver has to enforce that the field in the Go structure is non-nil at runtime

That's what I have off the top of my head. Any other footguns you have encountered?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment