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>
# 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!
}
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!, 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!
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
}
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
}
Schema - Circular Reference
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(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!]!
}
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
By Vilva Athiban
GraphQL: Anti Patterns in Schema design
- 636