GraphQL - Anti Patterns in Schema design
@vilvaathibanpb

- Vilva athiban p b
About me
JavaScript / GraphQL earns my bread
Travel and painting satisfies my thirst
Endless love for OSS but not committed to a single project for long 😉
Works @ Omio, A Search Engine for Travel
A Youtube series "Known Unknowns" - Vilva Athiban (Channel name)
@vilvaathibanpb

Let's begin
@vilvaathibanpb


what is anti patterN ?
1. A commonly used process, structure, or pattern of action that despite initially appearing to be an appropriate and effective response to a problem, has more bad consequences than good ones.
2. Another solution exists that is documented, repeatable, and proven to be effective.
@vilvaathibanpb

anti patters in schema design
- Nullable Fields
- Miscommunication in the Docs
- Lengthy arguments to Mutation
- Insufficient Mutation Response
- Allowing invalid inputs
- Schema - Circular Reference
- Massive data in response
Nullable Fields

#Sample Schema type Passenger { name: Name age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> #Breaks the UI without any errors during query
#Sample Schema type Passenger { name: Name age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> #Breaks the UI without any errors during query
#Sample Schema type Passenger { name: Name age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> #Breaks the UI without any errors during query
#Sample Schema type Passenger { name: Name age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> #Breaks the UI without any errors during query
#Sample Schema type Passenger { name: Name age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> #Breaks the UI without any errors during query
#Sample Schema type Passenger { name: Name! age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> # Throws error when queried # and doesnt break the UI
#Sample Schema type Passenger { name: Name! age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> # Throws error when queried # and doesnt break the UI
#Sample Schema type Passenger { name: Name! age: Int address: String } type Name { firstName: String secondName: String } <p>passenger.name.firstName</p> # Throws error when queried # and doesnt break the UI
Miscommunication in the Docs

# Sample schema type Passenger { id: ID! name: String! age: Int! address: String! passengerId: ID! }
# A single line, type-level description "Passenger details" type Passenger { """ a multi-line description the id is general user id """ id: ID! name: String! age: Int! address: String! "single line description: it is passenger id" passengerId: ID! }
# A single line, type-level description "Passenger details" type Passenger { """ a multi-line description the id is general user id """ id: ID! name: String! age: Int! address: String! "single line description: it is passenger id" passengerId: ID! }
# A single line, type-level description "Passenger details" type Passenger { """ a multi-line description the id is general user id """ id: ID! name: String! age: Int! address: String! "single line description: it is passenger id" passengerId: ID! }
# A single line, type-level description "Passenger details" type Passenger { """ a multi-line description the id is general user id """ id: ID! name: String! age: Int! address: String! "single line description: it is passenger id" passengerId: ID! }

Lengthy arguments to Mutation

type MutationResponse { status: String! } type Mutation { createPassenger(name: String!, age: String!, address: String!): MutationResponse } # Mutation in Frontend looks like: mutation PassengerMutation($name: String!, $age: String!, $address: String! ) { createPassenger(name: $name, age: $age, address: $address ) { status } }
type MutationResponse { status: String! } type Mutation { createPassenger(name: String!, age: String!, address: String!): MutationResponse } # Mutation in Frontend looks like: mutation PassengerMutation($name: String!, $age: String!, $address: String! ) { createPassenger(name: $name, age: $age, address: $address ) { status } }
type MutationResponse { status: String! } type Mutation { createPassenger(name: String!, age: String!, address: String!): MutationResponse } # Mutation in Frontend looks like: mutation PassengerMutation($name: String!, $age: String!, $address: String! ) { createPassenger(name: $name, age: $age, address: $address ) { status } }
type MutationResponse { status: String! } type Mutation { createPassenger(name: String!, age: String!, address: String!, city: String!): MutationResponse } # Mutation in Frontend looks like: mutation PassengerMutation($name: String!, $age: String!, $address: String!, city: String! ) { createPassenger(name: $name, age: $age, address: $address, city: $city ) { status } }
type MutationResponse { status: String! } type Mutation { createPassenger(name: String!, age: String!, address: String!, city: String!): MutationResponse } # Mutation in Frontend looks like: mutation PassengerMutation($name: String!, $age: String!, $address: String!, city: String! ) { createPassenger(name: $name, age: $age, address: $address, city: $city ) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! city: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! } type PassengerData { name: String! age: String! address: String! city: String! } type Mutation { createPassenger(passenger: PassengerData!): MutationResponse } #Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
Insufficient Mutation Response

type MutationResponse { status: String! } type Passenger { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: Passenger!): MutationResponse }
# Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status } }
type MutationResponse { status: String! id: ID! updatedPassenger: Passenger } type Passenger { name: String! age: String! address: String! } type Mutation { createPassenger(passenger: Passenger!): MutationResponse }
# Mutation in Frontend looks like: mutation PassengerMutation($passenger: PassengerData! ) { createPassenger(passenger: $passenger) { status id updatedPassenger } }
Allowing invalid inputs
-
Custom Scalars
-
Enums
Custom Scalars

Allowed scalars
- Int
- Float
- String
- Boolean
- ID
// Logic implementation for the Date type import { GraphQLScalarType } from 'graphql'; import { Kind } from 'graphql/language'; const resolverMap = { Date: new GraphQLScalarType({ name: 'Date', description: 'Date custom scalar type', parseValue(value) { return new Date(value); // value from the client }, serialize(value) { return value.getTime(); // value sent to the client }, }), };
scalar Date type MyType { created: Date }
scalar Date type MyType { created: Date }
scalar Date type MyType { created: Date }
ENUMS

# Enum of allowed countries: enum AllowedCountry { Germany USA Sweden Denmark } type Passenger { name: String! age: Int! country: AllowedCountry! #Country accepts only one of the values from the enum }
# Enum of allowed countries: enum AllowedCountry { Germany USA Sweden Denmark } type Passenger { name: String! age: Int! country: AllowedCountry! #Country accepts only one of the values from the enum }
# Enum of allowed countries: enum AllowedCountry { Germany USA Sweden Denmark } type Passenger { name: String! age: Int! country: AllowedCountry! #Country accepts only one of the values from the enum }
Schema - Circular Reference

type Passenger { name: String! location: Location! } type Location { country: String! passenger: Passenger! }
type Passenger { name: String! location: Location! } type Location { country: String! passenger: Passenger! }
query getPassenger { name location { counrty passenger { name location { country passenger { name location { country } } } } } }
Backup - graphql-depth-limit
Massive data in response

type Passenger { name: String! age: Int! } type Query { getAllPassenger: [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { getAllPassenger: [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { getAllPassenger(limit: Int): [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { getAllPassenger(limit: Int): [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { # a limit of 10 makes sure, when a limit is not passed, # it doesnt send more than 10 values getAllPassenger(limit: Int = 10): [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { # a limit of 10 makes sure, when a limit is not passed, # it doesnt send more than 10 values getAllPassenger(limit: Int = 10): [Passenger!]! }
type Passenger { name: String! age: Int! } type Query { # a limit of 10 makes sure, when a limit is not passed, # it doesnt send more than 10 values getAllPassenger(limit: Int = 10): [Passenger!]! }

thank you
QA Time:
@vilvaathibanpb

follow me on twitter
Please drop in some suggestions and feedback, so I can get better next time
GraphQL - Anti Patterns in Schema design @ vilvaathibanpb - Vilva athiban p b
GraphQL: Anti Patterns in Schema design
By Vilva Athiban
GraphQL: Anti Patterns in Schema design
- 690