A walk in GraphQL — Input Objects and Enums— Day 3
8 articles to learn GraphQL incrementally, keeping the implementation agnostic spirit of the SDL
Input Object
Simple operation arguments are relatively easy to define and control and scale as we saw on Day 02 but what if we need to pass along more than a simple Scalar?
What if we need to:
- pass along an object
- make sure all props belong to a specific type
- control the nullability for each prop and for the object itself
- reuse the same input and its validations across multiple operations
- add annotations to both the object and each individual prop
The SDL specifies a type for this case and states the following:
Some kinds of types, like Scalar and Enum types, can be used as both input types and output types; other kinds types can only be used in one or the other. Input Object types can only be used as input types. Object, Interface, and Union types can only be used as output types.
Now imagine you want to always be able to filter a certain Object Type by more than 1 field … let’s say 3
(⊙_⊙’) that’s absurd!!
And we’re talking about 14 lines of type definitions!!! Can you imagine scaling and maintaining this on a real world application?
This is one of the cases where Input Object Type shines.
Note that using the name input
for the argument and using the name InputCharacter
for the type definition is completely arbitrary, you can use whatever the team agrees to use.
Here’s a sample query you could request against the server for the type definition above.
and here the response
Fields validation
If you try to use an unknown field for the Input Object you’ll have an error from the server (and a warning from your preferred dev tool if you have one)
Response
And — as any type validation — the server will cry if a field value doesn’t match the defined type.
Non-nullable fields
but it’ll make it mandatory on every place you use it as input, otherwise it’ll throw an error like this: "Field InputCharacter.skill of required type String! was not provided."
… Use it wisely
Extending Input Objects?
Unfortunately you cannot do something like input InputCharacter extends Character
or input InputCharacter2 extends InputCharacter1
… you’ll have to explicitly define it yourself. Nothing stops you from creating it dynamically with your preferred programming language, in that case it’s suggested to generate a static output of your type definitions so you can leverage the development tool’s static code analysis (local or remote).
Nested Input Objects
Why is that? Remember? Because “Object, Interface, and Union types can only be used as output types.”
A note on reusing Input Objects
Input Objects are perfect for reuse, but it’s in your hands to determine “if, when and how” to reuse them. It’s perfectly fine to define an Input Object without reusing it!!
- Operations of the same type may evolve differently, reusing the same input might become a stick in the wheel.
- Avoid reusing the same Input Object for different operation types (query, mutation). They’re likely to have different requirements for non-nullable fields
Enum
This is a particularly interesting type that represents a finite set of values. Its values are represented as unquoted names (usually defined in all caps) that must be valid and unique within the set and there must be at least 1 value. They are (like Scalar Types) the leaf values in GraphQL.
On the sample query defined here we could pass any arbitrary value to the kind
field (e.g input: { kind: "whatever", homeland: "The Shire", skill: "the ringy thing" }
) without problems, the query can be performed and no errors will be thrown, probably returning no records. But, in the following example, we’ll add an enum definition forcing the value to be one of the set (or null in this case) and throw an error if it’s not.
Our query should be as follow
A secondary — but not less important — advantage of enums is that now the value is not only validated against your typedef and it’s auto-completed in your preferred dev tools, but it’s also abstracted! Means the client is no longer dealing with actual values but with abstracted representations which is more secure and scalable.
Language specific support for enums and internal values
The language specific implementation of enums will affect how you will have to handle the discrepancies on your side, and sometimes a backend forces a different value for an enum internally as opposite to the one in the public API.
In our example we have that discrepancy because the backend representation (internal representation) of the kind
field is half-elven
which doesn’t correspond to a valid name, so we used HALF_ELVEN
.
The strategy to solve these cases will depend on the language and the server app you’re using.
E.g. Apollo Server allows the addition of custom values to enums as shown below.
Exercise
For a given datasource (abstracted as json here) containing n
rows of skills
and n
rows of persons
we provided a sample implementation of a GraphQL server for each technology containing:
- a server app
- a schema
- a resolver map
- an entity model
- a db abstraction
The code contains the solution for previous exercises so you can have a starting point example.
Exercise requirements
- Update the type definition and the resolvers to be be able to perform the query operations listed below (can you provide other sample queries when your code is completed?).
- Discuss with someone else which would be the best way to use Input Objects and Enums and try some. (there’s always a tricky question)
Operations list
Provide the necessary code so that a user can perform the following query
operations (the argument’s values are arbitrary, your code should be able to respond for any valid value consistently):
The typedef should declare the following behavior and the resolvers should behave consistently for the arguments’ logic:
- All arguments for all queries are optional
- All arguments MUST be passed along as Input Objects
- All Input Object values MUST be either Scalar or Enum values
- All filtering rules must follow what specified on Day 02 exercise
Technologies
Select the exercise on your preferred technology:
Learning resources
GraphQL Spec (June 2018)
GraphQL Org
A walk in GraphQL Series at Medium
- Introduction
- Day 1 — Queries and Resolvers
- Day 2 — Arguments and Variables
- Day 3 — Input Objects and Enums
- Day 4 — Mutations
- Day 5 — Interfaces and Unions
- Day 6 — Extending SLD definitions
- Day 7 — Errors
Github Pages
Collaborators
- Ezequiel Alvarez — @ealvarezk
Reviewer - Ezequiel Tejerina — @quequitejerina
Python exercises contributor and reviewer - Franco Gribaudo — @fgriba
Java exercises contributor and reviewer - Cristian Buffa — @cristianbuffa & José Ignacio Aguilera — @jiaguilera
NetCore exercises contributors and reviewers - Javier Valderrama — @Jaxolotl
Author, JavaScript exercise contributor and reviewer