2134 stories
·
1 follower

A Promise of a Bright Future With Async Iterators, Generators, and Pipes, Part 1

3 Shares

Read the whole story
emrox
15 hours ago
reply
Hamburg, Germany
Share this story
Delete

Nulls in GraphQL: Cheatsheet

1 Share

Nullability in GraphQL is a controversial topic. Some say constant null checks are a nuisance, while others err on the side of paranoia. I say, UFO's are real. But, that's not the point. In this post, you'll hear both sides of the discussion, find a cheatsheet you can refer to later, and read about different approaches to nulls and errors by Apollo and Relay.

Nullability and error handling in GraphQL

So what's all the ruckus about? It all starts with GraphQL's unconventional approach to defaults and error handling.

In other typed languages, like TypeScript, Flow, Rust, or Haskell, all types are non-nullable by default, and you opt into nullability. But in GraphQL, all types are nullable by default, and you opt into non-nullability. In fact, Non-Null is a type that wraps your original type when you opt in.

TypeScript:

interface User {
  name?: string 
  name: string | null 
}

GraphQL:

type User {
  name: String! 
}

As if that's not enough fodder for DRAMZ, GraphQL breaks with the REST convention of relying on HTTP status codes for error handling. Even if there's an error in the GraphQL layer while executing your request, the response is still  200 OK 😲

Then how does the client know about errors? This is where nulls become relevant. The server returns a root field called errors, from which the client can extract them, and a root field called data that has all the requested fields. To top it off, any fields with errors have the value null.

All of this to say: all types in GraphQL are nullable by default in case of an error. GraphQL doesn't let a small hiccup get in the way of our data fetching: when there's an error with a field, we still get partial data.

We'll debate the pros and cons in a bit, including different approaches by Apollo and Relay. But first, the promised cheatsheet...

Nulls in the query

A GraphQL query can have fields and inputs, with or without variables.

Fields are always optional, whether nullable or not.

type User {
  name: String! 
  age: Int 
}

In inputs, such as field arguments, nullable types are always optional and non‐null types are always required.

type User {
  
  imageUrl(filter: String, size: String!): String
}
{
  user {
    imageUrl 
    imageUrl(size: null)  
    imageUrl(size: 'med') 
    imageUrl(filter: null, size: 'med') 
    imageUrl(filter: 'juno', size: 'med') 
  }
}

In addition, a variable of a nullable type cannot be provided to a non‐null argument.

query user($imageSize: String) { 
  imageUrl(size: $imageSize) 
}

Nulls in the response

If you get a null value for a nullable field from the server, there are four possibilities:

  1. The value is actually null or absent, e.g. user doesn't exist
  2. The value is hidden, e.g. user has blocked you (permission error)
  3. There was another error fetching the value
  4. An error or null value has bubbled up from a Non-Null child

Wait, what was that about bubbling up?

The GraphQL spec says that a null result on a Non-Null type bubbles up to the next nullable parent. If this bubbling never stops because everything is of Non-Null type, then the root data field is null.

Let's say name is nullable, but the resolver returns an error while fetching it:

data: { 
  user: { 
    name: null,
    age: 25,
    
 }
},
errors: [
  { 
    message: "Name could not be fetched",
    
  }
]

If name is non-nullable and user is nullable, this is what happens instead:

data: { 
  user: null
},
errors: [
  { 
    message: "Name could not be fetched",
    
  }
]

When fields are nullable, as in the first example, you still get partial data: even if you don't have the name, you can still show the age and other fields. When you mark fields as Non-Null, as in the second example, you forfeit your right to partial data.

How can I know the true meaning of null?

The four possibilities of null leave us with three questions:

  1. Do we have an actual absent value or an error?
  2. If it's an error, which field is it on?
  3. What kind of error is it?

And the answers lie in the errors list returned in the response.

If an error is thrown while resolving a field, it should be treated as though the field returned null, and an error must be added to the errors list in the response.
- GraphQL Spec

Anatomy of the errors list

The errors list must include message, locations, and path entries, with an optional extensions entry.

"errors": [
  {
    
    "message": "Name for user with ID 42 could not be fetched.",
    
    
    "locations": [ { "line": 3, "column": 5 } ],
    
    
    "path": [ "user", 0, "name" ],
    
    
    "extensions": {
      code: PERMISSION_ERROR,
      weatherOutside: 'weather'
    }
  }
]

The path entry answers our first two questions: whether the null result is intentional or due to an error, and what field it's on. The extensions entry answers our third question by allowing us to specify the type of error, and anything else we can possibly divine: the time of day when it happened, the weather outside, etc.

In the case of a  Non-Null field, the path entry still specifies the correct source of the error, even when the data returned points to the parent as the troublemaker.

data: { 
  user: null
},
errors: [
  { 
    message: "Name could not be fetched",
    path: [ "user", 0, "name" ]
    
  },
  
]

Now that we've got the cheatsheet down, let's talk about the pros and cons of nullable vs Non-Null types.

Benefits of nullable types

We've already covered one major benefit:

  • When the HTTP status code is 200 OK, we're able to get partial data from the server, despite errors on specific fields. But we still need a way to tell if something went wrong, which we can achieve with a null value on the erroneous field, along with an errors list (Btw, we'll discuss another solution to this – returning an Error type instead of null – in the Relay section below).

Other benefits:

  • Privacy when you want to obfuscate the reasons for null. Maybe you don't want the client to know whether you got a null on user because the user has blocked you or because the user simply doesn't exist. If you're a ghoster, this is the way to go.
  • If you're serving different clients from a single schema, nullable fields are more reliable, and easier to evolve. For example, if you remove a Non-Null field from the schema, a client that you have no control ever may break when it receives null for that field.
  • Coding defensively on the client side. Null check all the things!
if (user && user.location) return user.location.city;

Benefits of Non-Null types

If you find null checks cumbersome,  Non-Null is the type for you. Why?

  • You get guaranteed values. If you know that location is non-nullable, you can just do this:
if (user) return user.location.city;

If location returns null it will bubble up to user, and user.location.city will never execute. location is guaranteed to never be null, so you never need a null check on it.

  • You can combine Non-Null types with query type generation to make your code even more predictable. For example, with TypeScript, the generated code for a users query can be:
type GetUsersQuery = {
  users: Array<{
    __typename: "User",
    name: string 
    name: string | null 
  }
};
  • Easy error detection. If you get a null value for a non-nullable field, you know it's because of an error, since it can't be a legitimate absent value.

So how should I design my schema?

Though this is a hotly debated topic, the general recommendation is to make everything nullable except when it doesn't make sense, e.g. getUserById(id: ID!), or the value is guaranteed to exist, e.g. a primary key like id. But you can weigh the pros and cons above, and decide for yourself! (If you do, please let me know in the comments).

Depending on what GraphQL client you're using, you may be able to pick your strategy on a per-query basis, or come up with a different solution to error handling altogether.

Apollo: Everything is possible

When it comes to error handling, Apollo gives you options. Three options, in fact.

  • none (default): Treat GraphQL errors like network errors, ignore data
  • ignore: Get data, ignore errors
  • all: Get both data & errors

The all option matches the GraphQL spec, and will allow you to get partial data in case of an error. You can set the errorPolicy on a per-query basis, like so:

const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: 'all' });

Relay: All is good all the time

Relay is all about living the good life. A little GraphQL error here and there isn't enough to mess with Relay's zen thing.

That's right. Relay ignores GraphQL errors unless:

  • the fetch function provided to the Relay Network throws or returns an Error
  • the top-level data field isn't returned in the response

The Relay docs recommend modeling errors in your schema rather than returning null.

type Error {
  message: String!
}

type User {
  name: String | Error
}

For a detailed breakdown of how you might model this in your schema, check out this excellent blog post by Laurin Quast.

Conclusion

I hope this article has helped you master nulls in GraphQL. We covered:

  • GraphQL's approach to nullability and error handling
  • What nulls mean in GraphQL queries and responses
  • Pros & cons of nullable and Non-Null types
  • Different approaches to nulls and error handling by Apollo and Relay

Where do you stand in this discussion? What has been your experience with nullability? Let us know on Twitter or in the comments below!

Further Reading

Read the whole story
emrox
19 hours ago
reply
Hamburg, Germany
Share this story
Delete

Thinkpad DIY: Wie ich meinen alten Laptop fit für die Zukunft mache

1 Share
Read the whole story
emrox
2 days ago
reply
Hamburg, Germany
Share this story
Delete

A modified browser to aid your responsive web development

1 Share

It’ll mirror user interactions across 30+ devices and give you a single element inspector so you can make and inspect changes in one fell swoop. (Built with Electron.)

A modified browser to aid your responsive web development

Discuss on Changelog News

Read the whole story
emrox
5 days ago
reply
Hamburg, Germany
Share this story
Delete

‘What Time Is It in London?’

1 Share

Nilay Patel asked this of Siri on his Apple Watch. After too long of a wait, he got the correct answer — for London Canada. I tried on my iPhone and got the same result. Stupid and slow is heck of a combination.

You can argue that giving the time in London Ontario isn’t wrong per se, but that’s nonsense. If you had a human assistant and asked them “What’s the time in London?” and they honestly thought the best way to answer that question was to give you the time for the nearest London, which happened to be in Ontario or Kentucky, you’d fire that assistant. You wouldn’t fire them for getting that one answer wrong, you’d fire them because that one wrong answer is emblematic of a serious cognitive deficiency that permeates everything they try to do. You’d never have hired them in the first place, really, because there’s no way a person this stupid would get through a job interview. You don’t have to be particularly smart or knowledgeable to assume that “London” means “London England”, you just have to not be stupid.

Worse, I tried on my HomePod and Siri gave me the correct answer: the time in London England. I say this is worse because it exemplifies how inconsistent Siri is. Why in the world would you get a completely different answer to a very simple question based solely on which device answers your question? At least when most computer systems are wrong they’re consistently wrong.

I tried the same question on every other system I know where it should work: “What time is it in London?”

  • DuckDuckGo: got it right.
  • Google: got it right.
  • Alexa: got it right.
  • Bing: got it right.

Link: twitter.com/reckless/status/1263545804079464448

Read the whole story
emrox
5 days ago
reply
Hamburg, Germany
Share this story
Delete

The World's Longest Alphabetical Email Address

1 Share
The longest alphabetical email address is now offered to you for free! Look at the features of your mailbox below:

All FREE Mailbox comes with:

  • 6.0 MB Storage Capacity (Stores 6,000 messages)
  • Webmail Online Interface
  • The Longest Email Address in the Whole Wide World
 
Some Webforms do not work with your email

Due to the length of the email address, some webforms are not setup to handle such a long email address, and as such, they may deem your email address as invalid or even fraudulent! This is the only email address that would allow you to fail an online webform!

 
 
Some Email Software Cannot be configured

Again some email software may throw you an error message stating that the email address you enter is invalid simply because the email address is too long! This is also the only email address that would allow you to fail an email software as well!

 
 

People Cannot Remember your email address

It will be extremely hard for your friends or your coworkers to actually remember your email address because it is just too long! This is the only email address that would allow you to tease at your friends for not being able to remember your email address!

 
 

Companies think that your email address is fake

Companies nowadays love to send spam to people's email boxes, but imagine what they would think of your email address when they see it; they might think that it is fake and don't even bother sending you any spam mail! This is the only email address that would actually encourage companies to stop sending spam to you!

 
 

The World's Longest Email Address

Like the Great Wall of China, this is the longest alphabetical email address on Earth and it will remain so for the foreseeable years! Be extraordinary, get your free email account today!

 
Click here to sign up for a FREE email account
Read the whole story
emrox
5 days ago
reply
Hamburg, Germany
Share this story
Delete
Next Page of Stories