• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Writing a schema    {#flatbuffers_guide_writing_schema}
2================
3
4The syntax of the schema language (aka IDL, [Interface Definition Language][])
5should look quite familiar to users of any of the C family of
6languages, and also to users of other IDLs. Let's look at an example
7first:
8
9    // example IDL file
10
11    namespace MyGame;
12
13    attribute "priority";
14
15    enum Color : byte { Red = 1, Green, Blue }
16
17    union Any { Monster, Weapon, Pickup }
18
19    struct Vec3 {
20      x:float;
21      y:float;
22      z:float;
23    }
24
25    table Monster {
26      pos:Vec3;
27      mana:short = 150;
28      hp:short = 100;
29      name:string;
30      friendly:bool = false (deprecated, priority: 1);
31      inventory:[ubyte];
32      color:Color = Blue;
33      test:Any;
34    }
35
36    root_type Monster;
37
38(`Weapon` & `Pickup` not defined as part of this example).
39
40### Tables
41
42Tables are the main way of defining objects in FlatBuffers, and consist of a
43name (here `Monster`) and a list of fields. Each field has a name, a type, and
44optionally a default value. If the default value is not specified in the schema,
45it will be `0` for scalar types, or `null` for other types. Some languages
46support setting a scalar's default to `null`. This makes the scalar optional.
47
48Fields do not have to appear in the wire representation, and you can choose
49to omit fields when constructing an object. You have the flexibility to add
50fields without fear of bloating your data. This design is also FlatBuffer's
51mechanism for forward and backwards compatibility. Note that:
52
53-   You can add new fields in the schema ONLY at the end of a table
54    definition. Older data will still
55    read correctly, and give you the default value when read. Older code
56    will simply ignore the new field.
57    If you want to have flexibility to use any order for fields in your
58    schema, you can manually assign ids (much like Protocol Buffers),
59    see the `id` attribute below.
60
61-   You cannot delete fields you don't use anymore from the schema,
62    but you can simply
63    stop writing them into your data for almost the same effect.
64    Additionally you can mark them as `deprecated` as in the example
65    above, which will prevent the generation of accessors in the
66    generated C++, as a way to enforce the field not being used any more.
67    (careful: this may break code!).
68
69-   You may change field names and table names, if you're ok with your
70    code breaking until you've renamed them there too.
71
72See "Schema evolution examples" below for more on this
73topic.
74
75### Structs
76
77Similar to a table, only now none of the fields are optional (so no defaults
78either), and fields may not be added or be deprecated. Structs may only contain
79scalars or other structs. Use this for
80simple objects where you are very sure no changes will ever be made
81(as quite clear in the example `Vec3`). Structs use less memory than
82tables and are even faster to access (they are always stored in-line in their
83parent object, and use no virtual table).
84
85### Types
86
87Built-in scalar types are
88
89-   8 bit: `byte` (`int8`), `ubyte` (`uint8`), `bool`
90
91-   16 bit: `short` (`int16`), `ushort` (`uint16`)
92
93-   32 bit: `int` (`int32`), `uint` (`uint32`), `float` (`float32`)
94
95-   64 bit: `long` (`int64`), `ulong` (`uint64`), `double` (`float64`)
96
97The type names in parentheses are alias names such that for example
98`uint8` can be used in place of `ubyte`, and `int32` can be used in
99place of `int` without affecting code generation.
100
101Built-in non-scalar types:
102
103-   Vector of any other type (denoted with `[type]`). Nesting vectors
104    is not supported, instead you can wrap the inner vector in a table.
105
106-   `string`, which may only hold UTF-8 or 7-bit ASCII. For other text encodings
107    or general binary data use vectors (`[byte]` or `[ubyte]`) instead.
108
109-   References to other tables or structs, enums or unions (see
110    below).
111
112You can't change types of fields once they're used, with the exception
113of same-size data where a `reinterpret_cast` would give you a desirable result,
114e.g. you could change a `uint` to an `int` if no values in current data use the
115high bit yet.
116
117### Arrays
118
119Arrays are a convenience short-hand for a fixed-length collection of elements.
120Arrays can be used to replace the following schema:
121
122    struct Vec3 {
123        x:float;
124        y:float;
125        z:float;
126    }
127
128with the following schema:
129
130    struct Vec3 {
131        v:[float:3];
132    }
133
134Both representations are binary equivalent.
135
136Arrays are currently only supported in a `struct`.
137
138### Default, Optional and Required Values
139
140There are three, mutually exclusive, reactions to the non-presence of a table's
141field in the binary data:
142
1431.   Default valued fields will return the default value (as defined in the schema).
1442.   Optional valued fields will return some form of `null` depending on the
145     local language. (In a sense, `null` is the default value).
1463.   Required fields will cause an error. Flatbuffer verifiers would
147     consider the whole buffer invalid. See the `required` tag below.
148
149When writing a schema, values are a sequence of digits. Values may be optionally
150followed by a decimal point (`.`) and more digits, for float constants, or
151optionally prefixed by a `-`. Floats may also be in scientific notation;
152optionally ending with an `e` or `E`, followed by a `+` or `-` and more digits.
153Values can also be the keyword `null`.
154
155Only scalar values can have defaults, non-scalar (string/vector/table) fields
156default to `null` when not present.
157
158You generally do not want to change default values after they're initially
159defined. Fields that have the default value are not actually stored in the
160serialized data (see also Gotchas below). Values explicitly written by code
161generated by the old schema old version, if they happen to be the default, will
162be read as a different value by code generated with the new schema. This is
163slightly less bad when converting an optional scalar into a default valued
164scalar since non-presence would not be overloaded with a previous default value.
165There are situations, however, where this may be desirable, especially if you
166can ensure a simultaneous rebuild of all code.
167
168### Enums
169
170Define a sequence of named constants, each with a given value, or
171increasing by one from the previous one. The default first value
172is `0`. As you can see in the enum declaration, you specify the underlying
173integral type of the enum with `:` (in this case `byte`), which then determines
174the type of any fields declared with this enum type.
175
176Only integer types are allowed, i.e. `byte`, `ubyte`, `short` `ushort`, `int`,
177`uint`, `long` and `ulong`.
178
179Typically, enum values should only ever be added, never removed (there is no
180deprecation for enums). This requires code to handle forwards compatibility
181itself, by handling unknown enum values.
182
183### Unions
184
185Unions share a lot of properties with enums, but instead of new names
186for constants, you use names of tables. You can then declare
187a union field, which can hold a reference to any of those types, and
188additionally a field with the suffix `_type` is generated that holds
189the corresponding enum value, allowing you to know which type to cast
190to at runtime.
191
192It's possible to give an alias name to a type union. This way a type can even be
193used to mean different things depending on the name used:
194
195    table PointPosition { x:uint; y:uint; }
196    table MarkerPosition {}
197    union Position {
198      Start:MarkerPosition,
199      Point:PointPosition,
200      Finish:MarkerPosition
201    }
202
203Unions contain a special `NONE` marker to denote that no value is stored so that
204name cannot be used as an alias.
205
206Unions are a good way to be able to send multiple message types as a FlatBuffer.
207Note that because a union field is really two fields, it must always be
208part of a table, it cannot be the root of a FlatBuffer by itself.
209
210If you have a need to distinguish between different FlatBuffers in a more
211open-ended way, for example for use as files, see the file identification
212feature below.
213
214There is an experimental support only in C++ for a vector of unions (and
215types). In the example IDL file above, use [Any] to add a vector of Any to
216Monster table. There is also experimental support for other types besides
217tables in unions, in particular structs and strings. There's no direct support
218for scalars in unions, but they can be wrapped in a struct at no space cost.
219
220### Namespaces
221
222These will generate the corresponding namespace in C++ for all helper
223code, and packages in Java. You can use `.` to specify nested namespaces /
224packages.
225
226### Includes
227
228You can include other schemas files in your current one, e.g.:
229
230    include "mydefinitions.fbs";
231
232This makes it easier to refer to types defined elsewhere. `include`
233automatically ensures each file is parsed just once, even when referred to
234more than once.
235
236When using the `flatc` compiler to generate code for schema definitions,
237only definitions in the current file will be generated, not those from the
238included files (those you still generate separately).
239
240### Root type
241
242This declares what you consider to be the root table (or struct) of the
243serialized data. This is particularly important for parsing JSON data,
244which doesn't include object type information.
245
246### File identification and extension
247
248Typically, a FlatBuffer binary buffer is not self-describing, i.e. it
249needs you to know its schema to parse it correctly. But if you
250want to use a FlatBuffer as a file format, it would be convenient
251to be able to have a "magic number" in there, like most file formats
252have, to be able to do a sanity check to see if you're reading the
253kind of file you're expecting.
254
255Now, you can always prefix a FlatBuffer with your own file header,
256but FlatBuffers has a built-in way to add an identifier to a
257FlatBuffer that takes up minimal space, and keeps the buffer
258compatible with buffers that don't have such an identifier.
259
260You can specify in a schema, similar to `root_type`, that you intend
261for this type of FlatBuffer to be used as a file format:
262
263    file_identifier "MYFI";
264
265Identifiers must always be exactly 4 characters long. These 4 characters
266will end up as bytes at offsets 4-7 (inclusive) in the buffer.
267
268For any schema that has such an identifier, `flatc` will automatically
269add the identifier to any binaries it generates (with `-b`),
270and generated calls like `FinishMonsterBuffer` also add the identifier.
271If you have specified an identifier and wish to generate a buffer
272without one, you can always still do so by calling
273`FlatBufferBuilder::Finish` explicitly.
274
275After loading a buffer, you can use a call like
276`MonsterBufferHasIdentifier` to check if the identifier is present.
277
278Note that this is best for open-ended uses such as files. If you simply wanted
279to send one of a set of possible messages over a network for example, you'd
280be better off with a union.
281
282Additionally, by default `flatc` will output binary files as `.bin`.
283This declaration in the schema will change that to whatever you want:
284
285    file_extension "ext";
286
287### RPC interface declarations
288
289You can declare RPC calls in a schema, that define a set of functions
290that take a FlatBuffer as an argument (the request) and return a FlatBuffer
291as the response (both of which must be table types):
292
293    rpc_service MonsterStorage {
294      Store(Monster):StoreResponse;
295      Retrieve(MonsterId):Monster;
296    }
297
298What code this produces and how it is used depends on language and RPC system
299used, there is preliminary support for GRPC through the `--grpc` code generator,
300see `grpc/tests` for an example.
301
302### Comments & documentation
303
304May be written as in most C-based languages. Additionally, a triple
305comment (`///`) on a line by itself signals that a comment is documentation
306for whatever is declared on the line after it
307(table/struct/field/enum/union/element), and the comment is output
308in the corresponding C++ code. Multiple such lines per item are allowed.
309
310### Attributes
311
312Attributes may be attached to a declaration, behind a field, or after
313the name of a table/struct/enum/union. These may either have a value or
314not. Some attributes like `deprecated` are understood by the compiler;
315user defined ones need to be declared with the attribute declaration
316(like `priority` in the example above), and are
317available to query if you parse the schema at runtime.
318This is useful if you write your own code generators/editors etc., and
319you wish to add additional information specific to your tool (such as a
320help text).
321
322Current understood attributes:
323
324-   `id: n` (on a table field): manually set the field identifier to `n`.
325    If you use this attribute, you must use it on ALL fields of this table,
326    and the numbers must be a contiguous range from 0 onwards.
327    Additionally, since a union type effectively adds two fields, its
328    id must be that of the second field (the first field is the type
329    field and not explicitly declared in the schema).
330    For example, if the last field before the union field had id 6,
331    the union field should have id 8, and the unions type field will
332    implicitly be 7.
333    IDs allow the fields to be placed in any order in the schema.
334    When a new field is added to the schema it must use the next available ID.
335-   `deprecated` (on a field): do not generate accessors for this field
336    anymore, code should stop using this data. Old data may still contain this
337    field, but it won't be accessible anymore by newer code. Note that if you
338    deprecate a field that was previous required, old code may fail to validate
339    new data (when using the optional verifier).
340-   `required` (on a non-scalar table field): this field must always be set.
341    By default, fields do not need to be present in the binary. This is
342    desirable, as it helps with forwards/backwards compatibility, and
343    flexibility of data structures. By specifying this attribute, you make non-
344    presence in an error for both reader and writer. The reading code may access
345    the field directly, without checking for null. If the constructing code does
346    not initialize this field, they will get an assert, and also the verifier
347    will fail on buffers that have missing required fields. Both adding and
348    removing this attribute may be forwards/backwards incompatible as readers
349    will be unable read old or new data, respectively, unless the data happens to
350    always have the field set.
351-   `force_align: size` (on a struct): force the alignment of this struct
352    to be something higher than what it is naturally aligned to. Causes
353    these structs to be aligned to that amount inside a buffer, IF that
354    buffer is allocated with that alignment (which is not necessarily
355    the case for buffers accessed directly inside a `FlatBufferBuilder`).
356    Note: currently not guaranteed to have an effect when used with
357    `--object-api`, since that may allocate objects at alignments less than
358    what you specify with `force_align`.
359-   `force_align: size` (on a vector): force the alignment of this vector to be
360    something different than what the element size would normally dictate.
361    Note: Now only work for generated C++ code.
362-   `bit_flags` (on an unsigned enum): the values of this field indicate bits,
363    meaning that any unsigned value N specified in the schema will end up
364    representing 1<<N, or if you don't specify values at all, you'll get
365    the sequence 1, 2, 4, 8, ...
366-   `nested_flatbuffer: "table_name"` (on a field): this indicates that the field
367    (which must be a vector of ubyte) contains flatbuffer data, for which the
368    root type is given by `table_name`. The generated code will then produce
369    a convenient accessor for the nested FlatBuffer.
370-   `flexbuffer` (on a field): this indicates that the field
371    (which must be a vector of ubyte) contains flexbuffer data. The generated
372    code will then produce a convenient accessor for the FlexBuffer root.
373-   `key` (on a field): this field is meant to be used as a key when sorting
374    a vector of the type of table it sits in. Can be used for in-place
375    binary search.
376-   `hash` (on a field). This is an (un)signed 32/64 bit integer field, whose
377    value during JSON parsing is allowed to be a string, which will then be
378    stored as its hash. The value of attribute is the hashing algorithm to
379    use, one of `fnv1_32` `fnv1_64` `fnv1a_32` `fnv1a_64`.
380-   `original_order` (on a table): since elements in a table do not need
381    to be stored in any particular order, they are often optimized for
382    space by sorting them to size. This attribute stops that from happening.
383    There should generally not be any reason to use this flag.
384-   'native_*'.  Several attributes have been added to support the [C++ object
385    Based API](@ref flatbuffers_cpp_object_based_api).  All such attributes
386    are prefixed with the term "native_".
387
388
389## JSON Parsing
390
391The same parser that parses the schema declarations above is also able
392to parse JSON objects that conform to this schema. So, unlike other JSON
393parsers, this parser is strongly typed, and parses directly into a FlatBuffer
394(see the compiler documentation on how to do this from the command line, or
395the C++ documentation on how to do this at runtime).
396
397Besides needing a schema, there are a few other changes to how it parses
398JSON:
399
400-   It accepts field names with and without quotes, like many JSON parsers
401    already do. It outputs them without quotes as well, though can be made
402    to output them using the `strict_json` flag.
403-   If a field has an enum type, the parser will recognize symbolic enum
404    values (with or without quotes) instead of numbers, e.g.
405    `field: EnumVal`. If a field is of integral type, you can still use
406    symbolic names, but values need to be prefixed with their type and
407    need to be quoted, e.g. `field: "Enum.EnumVal"`. For enums
408    representing flags, you may place multiple inside a string
409    separated by spaces to OR them, e.g.
410    `field: "EnumVal1 EnumVal2"` or `field: "Enum.EnumVal1 Enum.EnumVal2"`.
411-   Similarly, for unions, these need to specified with two fields much like
412    you do when serializing from code. E.g. for a field `foo`, you must
413    add a field `foo_type: FooOne` right before the `foo` field, where
414    `FooOne` would be the table out of the union you want to use.
415-   A field that has the value `null` (e.g. `field: null`) is intended to
416    have the default value for that field (thus has the same effect as if
417    that field wasn't specified at all).
418-   It has some built in conversion functions, so you can write for example
419    `rad(180)` where ever you'd normally write `3.14159`.
420    Currently supports the following functions: `rad`, `deg`, `cos`, `sin`,
421    `tan`, `acos`, `asin`, `atan`.
422
423When parsing JSON, it recognizes the following escape codes in strings:
424
425-   `\n` - linefeed.
426-   `\t` - tab.
427-   `\r` - carriage return.
428-   `\b` - backspace.
429-   `\f` - form feed.
430-   `\"` - double quote.
431-   `\\` - backslash.
432-   `\/` - forward slash.
433-   `\uXXXX` - 16-bit unicode code point, converted to the equivalent UTF-8
434    representation.
435-   `\xXX` - 8-bit binary hexadecimal number XX. This is the only one that is
436     not in the JSON spec (see http://json.org/), but is needed to be able to
437     encode arbitrary binary in strings to text and back without losing
438     information (e.g. the byte 0xFF can't be represented in standard JSON).
439
440It also generates these escape codes back again when generating JSON from a
441binary representation.
442
443When parsing numbers, the parser is more flexible than JSON.
444A format of numeric literals is more close to the C/C++.
445According to the [grammar](@ref flatbuffers_grammar), it accepts the following
446numerical literals:
447
448-   An integer literal can have any number of leading zero `0` digits.
449    Unlike C/C++, the parser ignores a leading zero, not interpreting it as the
450    beginning of the octal number.
451    The numbers `[081, -00094]` are equal to `[81, -94]`  decimal integers.
452-   The parser accepts unsigned and signed hexadecimal integer numbers.
453    For example: `[0x123, +0x45, -0x67]` are equal to `[291, 69, -103]` decimals.
454-   The format of float-point numbers is fully compatible with C/C++ format.
455    If a modern C++ compiler is used the parser accepts hexadecimal and special
456    floating-point literals as well:
457    `[-1.0, 2., .3e0, 3.e4, 0x21.34p-5, -inf, nan]`.
458
459    The following conventions for floating-point numbers are used:
460    - The exponent suffix of hexadecimal floating-point number is mandatory.
461    - Parsed `NaN` converted to unsigned IEEE-754 `quiet-NaN` value.
462
463    Extended floating-point support was tested with:
464    - x64 Windows: `MSVC2015` and higher.
465    - x64 Linux: `LLVM 6.0`, `GCC 4.9` and higher.
466
467    For details, see [Use in C++](@ref flatbuffers_guide_use_cpp) section.
468
469-   For compatibility with a JSON lint tool all numeric literals of scalar
470    fields can be wrapped to quoted string:
471    `"1", "2.0", "0x48A", "0x0C.0Ep-1", "-inf", "true"`.
472
473## Guidelines
474
475### Efficiency
476
477FlatBuffers is all about efficiency, but to realize that efficiency you
478require an efficient schema. There are usually multiple choices on
479how to represent data that have vastly different size characteristics.
480
481It is very common nowadays to represent any kind of data as dictionaries
482(as in e.g. JSON), because of its flexibility and extensibility. While
483it is possible to emulate this in FlatBuffers (as a vector
484of tables with key and value(s)), this is a bad match for a strongly
485typed system like FlatBuffers, leading to relatively large binaries.
486FlatBuffer tables are more flexible than classes/structs in most systems,
487since having a large number of fields only few of which are actually
488used is still efficient. You should thus try to organize your data
489as much as possible such that you can use tables where you might be
490tempted to use a dictionary.
491
492Similarly, strings as values should only be used when they are
493truely open-ended. If you can, always use an enum instead.
494
495FlatBuffers doesn't have inheritance, so the way to represent a set
496of related data structures is a union. Unions do have a cost however,
497so an alternative to a union is to have a single table that has
498all the fields of all the data structures you are trying to
499represent, if they are relatively similar / share many fields.
500Again, this is efficient because non-present fields are cheap.
501
502FlatBuffers supports the full range of integer sizes, so try to pick
503the smallest size needed, rather than defaulting to int/long.
504
505Remember that you can share data (refer to the same string/table
506within a buffer), so factoring out repeating data into its own
507data structure may be worth it.
508
509### Style guide
510
511Identifiers in a schema are meant to translate to many different programming
512languages, so using the style of your "main" language is generally a bad idea.
513
514For this reason, below is a suggested style guide to adhere to, to keep schemas
515consistent for interoperation regardless of the target language.
516
517Where possible, the code generators for specific languages will generate
518identifiers that adhere to the language style, based on the schema identifiers.
519
520- Table, struct, enum and rpc names (types): UpperCamelCase.
521- Table and struct field names: snake_case. This is translated to lowerCamelCase
522  automatically for some languages, e.g. Java.
523- Enum values: UpperCamelCase.
524- namespaces: UpperCamelCase.
525
526Formatting (this is less important, but still worth adhering to):
527
528- Opening brace: on the same line as the start of the declaration.
529- Spacing: Indent by 2 spaces. None around `:` for types, on both sides for `=`.
530
531For an example, see the schema at the top of this file.
532
533## Gotchas
534
535### Schemas and version control
536
537FlatBuffers relies on new field declarations being added at the end, and earlier
538declarations to not be removed, but be marked deprecated when needed. We think
539this is an improvement over the manual number assignment that happens in
540Protocol Buffers (and which is still an option using the `id` attribute
541mentioned above).
542
543One place where this is possibly problematic however is source control. If user
544A adds a field, generates new binary data with this new schema, then tries to
545commit both to source control after user B already committed a new field also,
546and just auto-merges the schema, the binary files are now invalid compared to
547the new schema.
548
549The solution of course is that you should not be generating binary data before
550your schema changes have been committed, ensuring consistency with the rest of
551the world. If this is not practical for you, use explicit field ids, which
552should always generate a merge conflict if two people try to allocate the same
553id.
554
555### Schema evolution examples
556
557Some examples to clarify what happens as you change a schema:
558
559If we have the following original schema:
560
561    table { a:int; b:int; }
562
563And we extend it:
564
565    table { a:int; b:int; c:int; }
566
567This is ok. Code compiled with the old schema reading data generated with the
568new one will simply ignore the presence of the new field. Code compiled with the
569new schema reading old data will get the default value for `c` (which is 0
570in this case, since it is not specified).
571
572    table { a:int (deprecated); b:int; }
573
574This is also ok. Code compiled with the old schema reading newer data will now
575always get the default value for `a` since it is not present. Code compiled
576with the new schema now cannot read nor write `a` anymore (any existing code
577that tries to do so will result in compile errors), but can still read
578old data (they will ignore the field).
579
580    table { c:int; a:int; b:int; }
581
582This is NOT ok, as this makes the schemas incompatible. Old code reading newer
583data will interpret `c` as if it was `a`, and new code reading old data
584accessing `a` will instead receive `b`.
585
586    table { c:int (id: 2); a:int (id: 0); b:int (id: 1); }
587
588This is ok. If your intent was to order/group fields in a way that makes sense
589semantically, you can do so using explicit id assignment. Now we are compatible
590with the original schema, and the fields can be ordered in any way, as long as
591we keep the sequence of ids.
592
593    table { b:int; }
594
595NOT ok. We can only remove a field by deprecation, regardless of wether we use
596explicit ids or not.
597
598    table { a:uint; b:uint; }
599
600This is MAYBE ok, and only in the case where the type change is the same size,
601like here. If old data never contained any negative numbers, this will be
602safe to do.
603
604    table { a:int = 1; b:int = 2; }
605
606Generally NOT ok. Any older data written that had 0 values were not written to
607the buffer, and rely on the default value to be recreated. These will now have
608those values appear to `1` and `2` instead. There may be cases in which this
609is ok, but care must be taken.
610
611    table { aa:int; bb:int; }
612
613Occasionally ok. You've renamed fields, which will break all code (and JSON
614files!) that use this schema, but as long as the change is obvious, this is not
615incompatible with the actual binary buffers, since those only ever address
616fields by id/offset.
617<br>
618
619### Testing whether a field is present in a table
620
621Most serialization formats (e.g. JSON or Protocol Buffers) make it very
622explicit in the format whether a field is present in an object or not,
623allowing you to use this as "extra" information.
624
625FlatBuffers will not write fields that are equal to their default value,
626sometimes resulting in significant space savings. However, this also means we
627cannot disambiguate the meaning of non-presence as "written default value" or
628"not written at all". This only applies to scalar fields since only they support
629default values. Unless otherwise specified, their default is 0.
630
631If you care about the presence of scalars, most languages support "optional
632scalars." You can set `null` as the default value in the schema. `null` is a
633value that's outside of all types, so we will always write if `add_field` is
634called. The generated field accessor should use the local language's canonical
635optional type.
636
637Some `FlatBufferBuilder` implementations have an option called `force_defaults`
638that circumvents this "not writing defaults" behavior you can then use
639`IsFieldPresent` to query presence.
640
641Another option that works in all languages is to wrap a scalar field in a
642struct. This way it will return null if it is not present. This will be slightly
643less ergonomic but structs don't take up any more space than the scalar they
644represent.
645
646   [Interface Definition Language]: https://en.wikipedia.org/wiki/Interface_description_language
647