Slide 1

Slide 1 text

GraphQL nullability State of the union apidays 2023

Slide 2

Slide 2 text

Agenda ● Problem ● Solutions

Slide 3

Slide 3 text

2016

Slide 4

Slide 4 text

{ "data" { "video": { "id": "42", "title": "GraphQL 101", "uploadDate": "2023-12-03T22:26:46Z" } } }

Slide 5

Slide 5 text

2017

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

2017 Nullable types!

Slide 8

Slide 8 text

❀

Slide 9

Slide 9 text

But… Non-Null String String! Nullable String? String πŸ’ͺ πŸ€”

Slide 10

Slide 10 text

query GetVideo { video(id: "42") { title description viewCount comments(first: 5) { nodes { author text } } } } Nullable

Slide 11

Slide 11 text

??Why is everything nullable???? ????? ??

Slide 12

Slide 12 text

https://graphql.org/learn/best-practices/#nullability

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

Error null != Semantic null { "errors": [ { "message": "cannot resolve title", "path": "video.title" } ] "data": { "video": { "id": "42", "title": null } } }

Slide 15

Slide 15 text

Terminology ● Error null β—‹ Exceptional case β—‹ Has an error in the errors array ● Semantic null β—‹ Business case β—‹ No error in the errors array

Slide 16

Slide 16 text

@Composable fun VideoItem(video: Video) { Text(text = video.title!!) }

Slide 17

Slide 17 text

@Composable fun VideoItem(response: Response) { if (response.errors.isNotEmpty()) { Error() } else { Text(text = response.data.video.title!!) } }

Slide 18

Slide 18 text

2 months later

Slide 19

Slide 19 text

Post mortem ● New feature: video upload ● During the upload, a video doesn’t have a title yet ● This was a backward compatible schema change!

Slide 20

Slide 20 text

@Composable fun VideoItem(response: Response) { if (response.errors.isNotEmpty()) { Error() } else { if (response.data.video.title == null) { Text(text = "This video doesn't have a title yet") } else { Text(text = response.data.video.title) } } }

Slide 21

Slide 21 text

Different shades of error handling

Slide 22

Slide 22 text

There is more…

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

@Composable fun VideoItem(response: Response) { if (response.errors.any { it.path == "video.title" }) { Error() // Can't display a video without a title } else { if (response.data.video.title != null) { Text(text = "This video does not have a title yet") } else { Text(text = response.data.video.title!!) } if (response.errors.any { it.path == "video.viewCount" }) { // Skip viewCount } else { Text(text = "${response.data.video.viewCount!!} + views") } } } Partial data

Slide 25

Slide 25 text

10 fields ⇨ 1024 possibilities

Slide 26

Slide 26 text

Guessing game ● Semantic null? ● Error null? ● 🀷

Slide 27

Slide 27 text

GraphQL is missing types Your favorite type-safe language GraphQL string β†’ String! string | null β†’ String string | error β†’ String string | null | error β†’ String

Slide 28

Slide 28 text

Part II: solutions

Slide 29

Slide 29 text

Documentation type Video { """ The title of the Video null if the video has just been uploaded """ title: String """ Number of times this video has been watched Never null unless there is an error """ viewCount: Int }

Slide 30

Slide 30 text

Evolving the language

Slide 31

Slide 31 text

Nullability working group 2021 Client Controlled Nullability May 2023 sub working group Sep 2023 GraphQL conf True nullability Asterisk Interrobang Oct 2023 semantic-non-null @strictNullability @catch graphql/nullability-wg

Slide 32

Slide 32 text

Nullability working group 2021 Client Controlled Nullability May 2023 sub working group Sep 2023 GraphQL conf True nullability Asterisk Interrobang Oct 2023 semantic-non-null @strictNullability @catch graphql/nullability-wg

Slide 33

Slide 33 text

Asterisk string β†’ String! string | null β†’ don’t use string | error β†’ String* string | null | error β†’ String graphql/graphql-spec/pull/1048 Never a semantic null

Slide 34

Slide 34 text

Strict nullability string β†’ String! string | null β†’ don’t use string | error β†’ String string | null | error β†’ String? graphql/graphql-wg/discussions/1410 schema @strictNullability { query: Query } Never a semantic null

Slide 35

Slide 35 text

❀

Slide 36

Slide 36 text

Conclusion ● null is not always what it seems! ● be aware of the tradeoffs ● document nullability ● get involved!

Slide 37

Slide 37 text

Thank you! graphql/nullability-wg martinbonnin

Slide 38

Slide 38 text

Empty slide

Slide 39

Slide 39 text

Input nullability string β†’ String! string | null β†’ String string | absent β†’ String string | null | absent β†’ String graphql/graphql-spec/issues/476

Slide 40

Slide 40 text

Semantic non-null string β†’ String! string | null β†’ don’t use string | error β†’ !String string | null | error β†’ String graphql/graphql-spec/pull/1065

Slide 41

Slide 41 text

Interrobang string β†’ String! string | null β†’ don’t use string | error β†’ String!? string | null | error β†’ String graphql/graphql-spec/pull/1046

Slide 42

Slide 42 text

string β†’ String! string | null β†’ don’t use string | error β†’ String @semanticNonNull string | null | error β†’ String @semanticNonNull

Slide 43

Slide 43 text

Problem 2: what default? ● Fail the whole query β—‹ Easy β—‹ Fragile ● Handle field errors β—‹ Hard β—‹ Robust

Slide 44

Slide 44 text

Opting in error handling ● Start easy ● Add error handling progressively fragment VideoDetails on Video { video { title # opt-in error handling viewCount @catch } } facebook/relay/issues/4416

Slide 45

Slide 45 text

Opting in error handling fragment VideoDetails on Video { video { title # opt-in error handling viewCount @catch } } sealed interface Result { class Success(val value: T): Result class Failure(val error: error): Result } Video( val title: String val viewCount: Result ) apollographql/apollo-kotlin/pull/5405

Slide 46

Slide 46 text

Client Controlled Nullability graphql/graphql-wg/blob/main/rfcs/ClientControlledNullability.md fragment videoDetails on Video { video { # We need title to display a video title! # It's okay to fail viewCount viewCount? } }

Slide 47

Slide 47 text

string β†’ String string | null β†’ String? string | error β†’ String @throws string | null | error β†’ String? @throws Exceptions graphql/nullability-wg/discussions/39

Slide 48

Slide 48 text

Exceptions string β†’ don’t use string | null β†’ don’t use string | error β†’ String string | null | error β†’ String? graphql/nullability-wg/discussions/39

Slide 49

Slide 49 text

No content