|
| 1 | +# Semantic Non-Null |
| 2 | + |
| 3 | +> **Note:** The `@semanticNonNull` directive is a proposed RFC from the [GraphQL Nullability Working Group](https://github.com/graphql/nullability-wg) and is not yet part of the finalized GraphQL specification. The implementation may change as the proposal evolves. |
| 4 | +
|
| 5 | +## Overview |
| 6 | + |
| 7 | +The `@semanticNonNull` directive decouples nullability from error handling. It indicates that a field's resolver never intentionally returns null, but null may still be returned due to errors. |
| 8 | + |
| 9 | +This allows clients to understand which fields may be null only due to errors versus fields that may intentionally be null. |
| 10 | + |
| 11 | +## Enabling the Directive |
| 12 | + |
| 13 | +Since `@semanticNonNull` is a proposed-spec feature, you must explicitly opt-in by importing the directive in your schema: |
| 14 | + |
| 15 | +```elixir |
| 16 | +defmodule MyApp.Schema do |
| 17 | + use Absinthe.Schema |
| 18 | + |
| 19 | + # Import the proposed-spec @semanticNonNull directive |
| 20 | + import_types Absinthe.Type.BuiltIns.SemanticNonNull |
| 21 | + |
| 22 | + query do |
| 23 | + # ... |
| 24 | + end |
| 25 | +end |
| 26 | +``` |
| 27 | + |
| 28 | +Without this import, the `@semanticNonNull` directive will not be available in your schema. |
| 29 | + |
| 30 | +## Basic Usage |
| 31 | + |
| 32 | +### Using the Directive |
| 33 | + |
| 34 | +Apply the directive to field definitions: |
| 35 | + |
| 36 | +```elixir |
| 37 | +object :user do |
| 38 | + field :id, non_null(:id) |
| 39 | + |
| 40 | + # This field is semantically non-null - it only returns null on errors |
| 41 | + field :name, :string do |
| 42 | + directive :semantic_non_null |
| 43 | + end |
| 44 | + |
| 45 | + # This field may intentionally be null (no @semanticNonNull) |
| 46 | + field :nickname, :string |
| 47 | +end |
| 48 | +``` |
| 49 | + |
| 50 | +### Shorthand Notation |
| 51 | + |
| 52 | +Absinthe provides a shorthand for applying `@semanticNonNull`: |
| 53 | + |
| 54 | +```elixir |
| 55 | +object :user do |
| 56 | + # Using shorthand notation |
| 57 | + field :name, :string, semantic_non_null: true |
| 58 | + |
| 59 | + # For list fields, specify levels |
| 60 | + field :posts, list_of(:post), semantic_non_null: [0, 1] |
| 61 | +end |
| 62 | +``` |
| 63 | + |
| 64 | +## The Levels Argument |
| 65 | + |
| 66 | +The `levels` argument specifies which levels of the return type are semantically non-null: |
| 67 | + |
| 68 | +- `[0]` (default) - The field itself is semantically non-null |
| 69 | +- `[1]` - For list fields, the list items are semantically non-null |
| 70 | +- `[0, 1]` - Both the field and its items are semantically non-null |
| 71 | + |
| 72 | +### Examples |
| 73 | + |
| 74 | +```elixir |
| 75 | +object :user do |
| 76 | + # The name field is semantically non-null |
| 77 | + field :name, :string, semantic_non_null: true # Same as [0] |
| 78 | + |
| 79 | + # The posts list may be null, but items are semantically non-null |
| 80 | + field :posts, list_of(:post), semantic_non_null: [1] |
| 81 | + |
| 82 | + # Both the friends list AND its items are semantically non-null |
| 83 | + field :friends, list_of(:user), semantic_non_null: [0, 1] |
| 84 | +end |
| 85 | +``` |
| 86 | + |
| 87 | +## Introspection |
| 88 | + |
| 89 | +The directive adds introspection fields to `__Field`: |
| 90 | + |
| 91 | +```graphql |
| 92 | +{ |
| 93 | + __type(name: "User") { |
| 94 | + fields { |
| 95 | + name |
| 96 | + isSemanticNonNull |
| 97 | + semanticNonNullLevels |
| 98 | + } |
| 99 | + } |
| 100 | +} |
| 101 | +``` |
| 102 | + |
| 103 | +Response: |
| 104 | + |
| 105 | +```json |
| 106 | +{ |
| 107 | + "data": { |
| 108 | + "__type": { |
| 109 | + "fields": [ |
| 110 | + { |
| 111 | + "name": "name", |
| 112 | + "isSemanticNonNull": true, |
| 113 | + "semanticNonNullLevels": [0] |
| 114 | + }, |
| 115 | + { |
| 116 | + "name": "nickname", |
| 117 | + "isSemanticNonNull": false, |
| 118 | + "semanticNonNullLevels": null |
| 119 | + } |
| 120 | + ] |
| 121 | + } |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +## Client Considerations |
| 127 | + |
| 128 | +Clients can use `@semanticNonNull` information to: |
| 129 | + |
| 130 | +- Automatically throw errors when semantically non-null fields return null |
| 131 | +- Generate stricter types in code generation tools |
| 132 | +- Improve developer experience with better nullability handling |
| 133 | + |
| 134 | +Apollo Client and other modern GraphQL clients are adding support for this directive. Consult your client's documentation for specific integration details. |
| 135 | + |
| 136 | +## See Also |
| 137 | + |
| 138 | +- [GraphQL Nullability Working Group](https://github.com/graphql/nullability-wg) |
| 139 | +- [Errors Guide](errors.md) for error handling in Absinthe |
0 commit comments