warpgrapher 0.11.2

Automate web service creation with GraphQL and Graph Databases
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Relationships - Warpgrapher Book</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../warpgrapher/intro.html"><strong aria-hidden="true">1.</strong> Warpgrapher</a></li><li class="chapter-item expanded "><a href="../warpgrapher/quickstart.html"><strong aria-hidden="true">2.</strong> Quickstart</a></li><li class="chapter-item expanded "><a href="../integrations/intro.html"><strong aria-hidden="true">3.</strong> Server Integration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../integrations/actix.html"><strong aria-hidden="true">3.1.</strong> Actix Web</a></li><li class="chapter-item expanded "><a href="../integrations/lambda.html"><strong aria-hidden="true">3.2.</strong> AWS Lambda</a></li></ol></li><li class="chapter-item expanded "><a href="../configuration/intro.html"><strong aria-hidden="true">4.</strong> Configuration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../configuration/databases.html"><strong aria-hidden="true">4.1.</strong> Databases</a></li><li class="chapter-item expanded "><a href="../configuration/formats.html"><strong aria-hidden="true">4.2.</strong> Formats</a></li><li class="chapter-item expanded "><a href="../configuration/types.html"><strong aria-hidden="true">4.3.</strong> Types</a></li><li class="chapter-item expanded "><a href="../configuration/relationships.html" class="active"><strong aria-hidden="true">4.4.</strong> Relationships</a></li></ol></li><li class="chapter-item expanded "><a href="../api/intro.html"><strong aria-hidden="true">5.</strong> CRUD API Usage Examples</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../api/node_create.html"><strong aria-hidden="true">5.1.</strong> Node Create</a></li><li class="chapter-item expanded "><a href="../api/node_read.html"><strong aria-hidden="true">5.2.</strong> Node Read</a></li><li class="chapter-item expanded "><a href="../api/node_update.html"><strong aria-hidden="true">5.3.</strong> Node Update</a></li><li class="chapter-item expanded "><a href="../api/node_delete.html"><strong aria-hidden="true">5.4.</strong> Node Delete</a></li><li class="chapter-item expanded "><a href="../api/rel_create.html"><strong aria-hidden="true">5.5.</strong> Relationship Create</a></li><li class="chapter-item expanded "><a href="../api/rel_read.html"><strong aria-hidden="true">5.6.</strong> Relationship Read</a></li><li class="chapter-item expanded "><a href="../api/rel_update.html"><strong aria-hidden="true">5.7.</strong> Relationship Update</a></li><li class="chapter-item expanded "><a href="../api/rel_delete.html"><strong aria-hidden="true">5.8.</strong> Relationship Delete</a></li></ol></li><li class="chapter-item expanded "><a href="../engine/intro.html"><strong aria-hidden="true">6.</strong> Engine Features</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../engine/endpoints_static.html"><strong aria-hidden="true">6.1.</strong> Static Endpoints</a></li><li class="chapter-item expanded "><a href="../engine/endpoints_defined.html"><strong aria-hidden="true">6.2.</strong> Defined Endpoints</a></li><li class="chapter-item expanded "><a href="../engine/dynamic_props.html"><strong aria-hidden="true">6.3.</strong> Dynamic Props</a></li><li class="chapter-item expanded "><a href="../engine/dynamic_rels.html"><strong aria-hidden="true">6.4.</strong> Dynamic Relationships</a></li><li class="chapter-item expanded "><a href="../engine/context_request.html"><strong aria-hidden="true">6.5.</strong> Request Context</a></li><li class="chapter-item expanded "><a href="../engine/input_validation.html"><strong aria-hidden="true">6.6.</strong> Input Validation</a></li><li class="chapter-item expanded "><a href="../engine/event_handlers.html"><strong aria-hidden="true">6.7.</strong> Event Handlers</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Warpgrapher Book</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="relationships"><a class="header" href="#relationships">Relationships</a></h1>
<p>The <a href="../warpgrapher/quickstart.html">Quickstart</a> example used a very simple model with only one type, containing one property. The <a href="./types.html">Types</a> section explored configuration file format and the resulting GraphQL schema in more detail. However, Warpgrapher can generate create, read, update, and delete operations for relationships between types as well. The configuration below includes describes two types and a relationship between them.</p>
<pre><code class="language-yaml">version: 1
model:
  - name: User
    props:
      - name: email
        type: String
        required: false
  - name: Organization
    props:
      - name: name
        type: String
        required: false
    rels:
      - name: members
        nodes: [User]
        list: true
        props:
          - name: joinDate
            type: String
            required: false
</code></pre>
<p>The configuration above adds a second type, called <code>Organization</code>. The definition of the organization type contains the <code>rels</code> attribute, which was not seen in the earlier example. The <code>rels</code> attribute contains a list of permissible relationships between nodes. In this case, the configuration adds a <code>members</code> relationship from nodes of of the <code>Organization</code> type to nodes of the <code>User</code> type, indicating that certain users are members of an organization. The <code>name</code> attribute in the configuration is the name of the relationship and must be unique within the scope of that type. The <code>nodes</code> attribute is a list of other types that may be at the destination end of the relationship. In this case, the only type at may be a member is the <code>User</code> type, but in other use cases, the destination node might be allowed to be one of several types.  Lastly, the <code>list</code> attribute is <code>true</code>, indicating that an <code>Organization</code> may have more than one member.</p>
<h2 id="relationship-configuration"><a class="header" href="#relationship-configuration">Relationship Configuration</a></h2>
<p>The example configuration above is fairly simple, and does not make use of several optional attributes. The definition below shows the full set of configuration options that are permissible on a relationship.</p>
<pre><code class="language-yaml">model:
  - name: String
    rels:
      - name: String
        nodes: [String]  # Values in the list must be other types in the model
        list: Boolean
        props:
          - name: String
            uses:
              create: Boolean
              query: Boolean
              update: Boolean
              output: Boolean
            type: String  # Boolean | Float | ID | Int | String
            required: Boolean
            list: Boolean
            resolver: String
            validator: String
        endpoints:
          read: Boolean
          create: Boolean
          update: Boolean
          delete: Boolean
        resolver: String
</code></pre>
<p>The snippet above shows that relationships are defined in a list under the <code>rels</code> attribute within a type definition. Each relationship has a <code>name</code> that must be unique within the scope of that type. The <code>nodes</code> attribute is a list of name of types within the model that can appear as destination nodes in the relationship. Note that the a type may appear in its own relationship's <code>nodes</code> lists. A node is permitted to have relationships to nodes of the same type.</p>
<p>If the <code>list</code> attribute is <code>true</code>, then a node may have relationships of the same type to multiple destination nodes, modeling one-to-many relationships. If <code>list</code> is false, then the node may only have a single relationship of that type, to a single destination node.</p>
<p>The <code>props</code> attribute on a relationship works the same way that the <code>props</code> attribute works on nodes, except that the properties are associated with the relationship rather than with the node.  See the description of the <code>props</code> attribute in the section on <a href="./types.html">types</a> for more details.</p>
<p>Similarly, the <code>endpoints</code> attribute on relationships works the same way that it does on nodes. The individual boolean attributes within the <code>endpoints</code> object control whether Warpgrapher generates GraphQL schema elements for create, read, update, and delete operations. Just as with types, the default for all the boolean values is <code>true</code>, meaning that by default Warpgrapher creates schema elements and resolvers for all CRUD operations.</p>
<p>Lastly, the <code>resolver</code> attribute is also similar to the attribute of the same name on property definitions. The string in the <code>resolver</code> attribute is mapped to a custom-written Rust function provided when setting up the Warpgrapher engine. This allows systems using Warpgrapher to control the behavior of resolving some relationships. Use cases for this include dynamically-generated relationships that are computed at query time rather than being stored in the back-end data store.</p>
<h2 id="generated-schema"><a class="header" href="#generated-schema">Generated Schema</a></h2>
<p>This section describes each of the GraphQL schema elements that Warpgrapher generates for CRUD operations on relationships. Discussion of the schema elements related solely to types, absent relationships, was covered previously in the <a href="./types.html">types section</a>.</p>
<h3 id="queries-in-a-model-with-relationships"><a class="header" href="#queries-in-a-model-with-relationships">Queries in a Model with Relationships</a></h3>
<p>The top level GraphQL query object includes three (3) queries. This should make intuitive sense. The model has two nodes, <code>Organization</code> and <code>User</code>, and one relationship, the <code>OrganizationMembers</code> relationship from a source organization to a destination user that is a member of that organization. Warpgrapher's generated schema allows for querying either node type or the relationship between them. As will be discussed in detail below, the inputs to these query operations have a recursive structure, so that using the top level query for the relationship, it is possible to filter based on the properties of the source or destination nodes. Similarly, when querying for a node type, it is possible to add search parameters related to relationships, the destinations of those relationships, and so on.</p>
<pre><code>type Query {
  Organization(
    input: OrganizationQueryInput,
    options: OrganizationOptions
  ): [Organization!]

  OrganizationMembers(
    input: OrganizationMembersQueryInput,
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]

  User(input: UserQueryInput, options: UserOptions): [User!]

  _version: String
}
</code></pre>
<h3 id="querying-for-a-relationship"><a class="header" href="#querying-for-a-relationship">Querying for a Relationship</a></h3>
<p>In the top level GraphQL query, note that a new query, called <code>OrganizationMembers</code> has been generated for the relationship. This query has an input parameter, <code>OrganizationMembersQueryInput</code> that provides search query arguments to select the set of relationships to be returned.</p>
<p>The <code>OrganizationMembersQueryInput</code> query parameter, defined below, provides a means to search for a given instance of a relationship.  It is possible to search based on an <code>id</code> or set of IDs, and the <code>joinDate</code> attribute allows queries based on the properties on the relationship. In addition to using the <code>id</code> or another property on the relationship, the <code>OrganizationMembersQueryInput</code> parameter also includes a <code>src</code> and a <code>dst</code> attribute. These attributes allow Warpgrapher clients to search for relationships based on properties of the source or destination nodes joined by the relationship.</p>
<pre><code>input OrganizationMembersQueryInput {
  dst: OrganizationMembersDstQueryInput
  id: StringQueryInput
  joinDate: StringQueryInput
  src: OrganizationMembersSrcQueryInput
}
</code></pre>
<p>The two input objects for the <code>src</code> and <code>dst</code> input objects are shown below. Note that for the source query input, the only attribute is an <code>Organization</code> attribute that is an <code>OrganizationQueryInput</code> and that for the destination, the only attribute is a <code>User</code> attribute that is a <code>UserQueryInput</code>. There are two important observations here.  First, the reason for having the <code>OrganizationMembersDstQueryInput</code> object is that a relationship might have more than one node type as a possible destination. When building the GraphQL schema, Warpgrapher has to allow for the client to query any of those possible destination nodes. In this example, the only type of destination node is a <code>User</code>, so that's the only possibility shown below. If the nodes list had more types of nodes, any of those node types could be queried through the <code>OrganizationMembersDstQueryInput</code>.  The second observation is that both the <code>OrganizationQueryInput</code> and the <code>UserQueryInput</code> inputs are the same input parameters used to query for a set of nodes in the <code>Organization</code> and <code>User</code> root level GraphQL queries shown above.</p>
<pre><code>input OrganizationMembersSrcQueryInput {
  Organization: OrganizationQueryInput
}

input OrganizationMembersDstQueryInput {
  User: UserQueryInput
}
</code></pre>
<p>We'll come back to the node-based query input in a moment, in the section below on Querying for a Node. First, the code snippet below shows the schema for output from the relationship query. The relationship includes four attributes, a unique identifier for the relationship called <code>id</code>, <code>joinDate</code> for the property configured on the relationship, and <code>src</code> and <code>dst</code> attributes that represent the source and destination nodes respectively.</p>
<pre><code>type OrganizationMembersRel {
  dst: OrganizationMembersNodesUnion!
  id: ID!
  joinDate: String
  src: Organization!
}
</code></pre>
<p>The <code>src</code> attribute in the <code>OrganizationMembersRel</code> output type is an <code>Organization</code> type, which is exactly the same output type used for node queries, and so will be covered in the section on querying for nodes, below.  The <code>dst</code> attribute is a little more complex. Recall from the description of the configuration schema that Warpgrapher may connect from a source node type to a destination that can be one of many node types. A GraphQL union type is used to represent the multiple destination node types that may exist.  As shown in the schema snippet below, in this example of <code>OrganizationMembersNodesUnion</code>, there is only a single destination node type of User. A more complex configuration might have multiple node types in the union.</p>
<pre><code>union OrganizationMembersNodesUnion = User
</code></pre>
<p>Note that the <code>User</code> type is the same type that is used to return users in queries for nodes.</p>
<p>The <code>options</code> argument, described above as an argument for the <code>OrganizationMembers</code> query as a whole, is of type <code>OrganizationMembersOptions</code>. The <code>OrganizationMembersOptions</code> type has a single property, called <code>sort</code>, which is a list of zero or more <code>OrganizationMembersSort</code> objects. Each <code>OrganizationMembersSort</code> object has two enumeration properties, <code>direction</code> and <code>orderBy</code>.</p>
<pre><code>type OrganizationMembersOptions {
  sort: [OrganizationMembersSort!]
}

type UserSort {
  direction: DirectionEnum
  orderBy: OrganizationMembersOrderByEnum!
}

enum DirectionEnum {
  ascending
  descending
}

enum OrganizationMembersOrderByEnum {
  id
  dst:email
}
</code></pre>
<p>The <code>OrganizationMembersOrderByEnum</code> has variant values for each of the properties (but not relationships) on the OrganizationMembers relationship, though in this case that's only the <code>id</code> property. Additionally, the enum has variants for each of the properties on the destination object, allowing the results to be sorted either by properties on the relationship itself, or those on the destination object. By including one or more values in the <code>sort</code> array provided to <code>UserOptions</code>, it is possible to sort results coming back from Warpgrapher. The <code>direction</code> property determines whether the results are returned in ascending or descending sort order. The <code>orderBy</code> field determines on which property the results are sorted.  For example, above, an <code>orderBy</code> field with a value of <code>dst:email</code> would sort the organization member's relationship results in alphabetical order of member email addresses.  If the <code>sort</code> array contains more than one value, then resorts groups of results with the same first sort key are further sorted by the second key, and so on.  For example, a <code>sort</code> array might have entries for <code>joinDate</code> and then <code>name</code> to sort first by the date someone joined, and alphabetically for all people who joined on the same date.</p>
<h3 id="querying-for-a-node"><a class="header" href="#querying-for-a-node">Querying for a Node</a></h3>
<p>The root GraphQL <code>Query</code> object has queries for each of the node types in the configuration.  To see how relationships affect node queries, have a look at the <code>Organization</code> query, beginning with the <code>OrganizationQueryInput</code> definition in the snippet below. In addition to the <code>id</code> and <code>name</code> attributes for searching based on the scalar properties of the type, the schema also includes a <code>members</code> attribute, of type <code>OrganizationMembersQueryInput</code>.  This is the same input object described above that's used in the root level query for the <code>OrganizationMembers</code> relationship. This recursive schema structure is really quite powerful, as it allows the client to query for nodes based on a combination of the node's property values, the values of properties in the relationships that it has, and the values of properties in the destination nodes at the other end of those relationships, to any level of depth.  For example, it would be easy to construct a query that retrieves all of the organizations that contain a particular user as a member. For examples of relationship-based queries, see the chapter on <a href="../api/intro.html">API usage</a>.</p>
<pre><code>input OrganizationQueryInput {
  id: StringQueryInput
  members: OrganizationMembersQueryInput
  name: StringQueryInput
}
</code></pre>
<p>Relationshps information can be navigated in the output type for the node, as well. The <code>Organization</code> output type shown in the snippet below includes both the scalar properties on the type, the <code>id</code> and <code>name</code>, as well as the relationship to the <code>members</code> of the Organization.  The <code>members</code> attribute includes an input of type <code>OrganizationMembersQueryInput</code>. This is the same input type that is used to query for members relationships from the GraphQL root query, desribed above. This means that when retrieving Organization nodes, it's possible to filter the set of members that you want to retrieve in a nested query. Again, the recursive structure of the schema generated by Warpgrapher allows you the flexibility to query to any level of depth in a sub-graph that is needed.</p>
<pre><code>type Organization {
  id: ID!
  members(input: OrganizationMembersQueryInput): [OrganizationMembersRel!]
  name: String
}
</code></pre>
<h3 id="mutations-in-a-model-with-relationships"><a class="header" href="#mutations-in-a-model-with-relationships">Mutations in a Model with Relationships</a></h3>
<p>The GraphQL schema's top level mutation object contains nine (9) mutations. This should make intuitive sense. There are three mutations (create, update, and delete), and three kinds of things that can be mutated: organization nodes, user nodes, and membership relationships between organizations and nodes. There are quite a few nested input and output types contributing to these mutations. The high-level principle to keep in mind is that Warpgrapher allows recursive operations that support manipulation of whole sub-graphs at a time. For example, node mutations have nested input objects that allow manipulation of the relationships on those nodes, and the destination nodes at the end of those relationships, and so on.</p>
<pre><code>type Mutation {
  OrganizationCreate(
    input: OrganizationCreateMutationInput!
    options: OrganizationOptions
  ): Organization
  OrganizationDelete(input: OrganizationDeleteInput!, options: OrganizationOptions): Int
  OrganizationMembersCreate(
    input: OrganizationMembersCreateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationMembersDelete(
    input: OrganizationMembersDeleteInput!
    options: OrganizationMembersOptions
  ): Int
  OrganizationMembersUpdate(
    input: OrganizationMembersUpdateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationUpdate(
    input: OrganizationUpdateInput!
    options: OrganizationOptions
  ): [Organization!]
  UserCreate(input: UserCreateMutationInput!, options: UserOptions): User
  UserDelete(input: UserDeleteInput!, options: UserOptions): Int
  UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!]
}
</code></pre>
<h3 id="mutating-a-relationship"><a class="header" href="#mutating-a-relationship">Mutating a Relationship</a></h3>
<h4 id="creating-a-relationship"><a class="header" href="#creating-a-relationship">Creating a Relationship</a></h4>
<p>The snippet below contains the input for creation of one or more OrganizationMembers relationships. There are two attributes, <code>MATCH</code> and <code>CREATE</code>. The <code>MATCH</code> attribute is used to identify the organization or organizations that should be matched as the source of the relationship(s) to be created. It has the same type, <code>OrganizationQueryInput</code> that is used to query for nodes using the <code>Organization</code> query under the GraphQL <code>Query</code> root described above.  The match query may select more than one node, allowing similar relationships to be created in bulk. Matching existing source nodes is the only option when creating a relationship. If it is necessary to create the node at the source end of the relationship, see the node creation operation, in this case <code>OrganizationCreate</code> instead.</p>
<pre><code>input OrganizationMembersCreateInput {
  CREATE: [OrganizationMembersCreateMutationInput!]
  MATCH: OrganizationQueryInput
}
</code></pre>
<p>The <code>CREATE</code> attribute has a type of <code>OrganizationMembersCreateMutationInput</code>. That input structure is shown in the schema snippet below. It includes the <code>joinDate</code> attribute on the relationship. The <code>id</code> object is accepted as an input to facilitate offline operation, in which the client may need to choose the unique identifier for the relationship. If the client does not choose the identifier, it will be randomly assigned by the Warpgrapher service.</p>
<pre><code>input OrganizationMembersCreateMutationInput {
  dst: OrganizationMembersNodesMutationInputUnion!
  id: ID
  joinDate: String
}
</code></pre>
<p>The <code>dst</code> property in the <code>OrganizationMembersCreateMutationInput</code> above is of type <code>OrganizationMembersNodesMutationInputUnion</code>, which is included in the schema snippet below. Don't be intimidated by the lengthy name of the union type. Recall that in the configuration above, the destination type of a relationship is allowed to have more than one type. In this configuration, it only has one type, but the <code>OrganizationMembersNodesMutationInputUnion</code> is what allows the destination of the relationship to have multiple types. In this case, the only option is <code>User</code>, with a type of <code>UserInput</code>.</p>
<pre><code>input OrganizationMembersNodesMutationInputUnion {
  User: UserInput
}
</code></pre>
<p>The <code>UserInput</code> type, which provides the destination node for the relationship(s) to be created, has two attributes. When using the <code>EXISTING</code> attribute, Warpgrapher search the graph database for a set of nodes matching the <code>UserQueryInput</code> search criteria and uses the results as the destination nodes for creation of the relationship(s). Note that this <code>UserQueryInput</code> type is the same input type that is used to query for users in the user query under the GraphQL root <code>Query</code>. No matter where in the recursive hierarchy, searhing for <code>User</code> nodes always uses the same input.  The <code>NEW</code> attribute creates a new <code>User</code> node as the destination of the relationship. Note that the <code>UserCreateMutationInput</code> input type is the same input type used to create a <code>User</code> node in the <code>UserCreate</code> mutation under the GraphQL root <code>Mutation</code> object.</p>
<pre><code>input UserInput {
  EXISTING: UserQueryInput
  NEW: UserCreateMutationInput
}
</code></pre>
<p>The output of creating one or more relationships, <code>OrganizationMembersRel</code>, is the same output type returned from querying for the organization's members relationship, as was described in the section on queries, above. It contains the newly created relationship.</p>
<h4 id="updating-a-relationship"><a class="header" href="#updating-a-relationship">Updating a Relationship</a></h4>
<p>The input for a relationship update mutation, <code>OrganizationMembersUpdateInput</code> is shown in the schema snippet below. The update input consists of two parts. The <code>MATCH</code> attribute is a query input to identify the relationships that should be updated. Note that the match input type, <code>OrganizationMembersQueryInput</code> is the same input type used to provide search parameters when searching for relationships under the <code>OrganizationMembers</code> query under the GraphQL root <code>Query</code> object.  The <code>SET</code> attribute is used to describe the changes that should be made to values in the relationship(s) matched by the <code>MATCH</code> parameter, and potentially the sub-graph beneath.</p>
<pre><code>input OrganizationMembersUpdateInput {
  MATCH: OrganizationMembersQueryInput
  SET: OrganizationMembersUpdateMutationInput!
}
</code></pre>
<p>The <code>SET</code> input is of type <code>OrganizationMembersUpdateMutationInput</code>, shown in the snippet below. The <code>joinDate</code> attribute is the same input type used during relationship creation operations, described in the section above. The <code>src</code> and <code>dst</code> attributes allow a single update to provide new values not only for the relationship properties, but also properties on the source and destination nodes at the ends of the relationship.</p>
<pre><code>input OrganizationMembersUpdateMutationInput {
  dst: OrganizationMembersDstUpdateMutationInput
  joinDate: String
  src: OrganizationMembersSrcUpdateMutationInput
}
</code></pre>
<p>The source and destination node input types are shown in the schema snippets below. Note that the types, <code>OrganizationUpdateMutationInput</code> and <code>UserUpdateMutationInput</code> are the same input types used for the <code>SET</code> attributes in the single node update operation, described in in the section on single-node mutation operations below. Thus, we have hit the point where the GraphQL schema structure that Warpgrapher generates is recursive. A relationship update mutation can update the properties on the relationship, as described just above, or using this recursive input structure, reach down into the source and destination nodes at the ends of the relationship and edit their properties as well.</p>
<pre><code>input OrganizationMembersSrcUpdateMutationInput {
  Organization: OrganizationUpdateMutationInput
}

input OrganizationMembersDstUpdateMutationInput {
  User: UserUpdateMutationInput
}
</code></pre>
<p>The output for updating one or more relationships, <code>OrganizationMembersRel</code>, is the same output type returned from querying for an organization's members relationship, as was described in the section on queries, above. For update operations, it returns the list of relationships that were updated in the mutation.</p>
<h4 id="deleting-a-relationship"><a class="header" href="#deleting-a-relationship">Deleting a Relationship</a></h4>
<p>The input for a relationship delete mutation, <code>OrganizationMembersDeleteInput</code>, is shown in the schema snippet below. The <code>MATCH</code> attribute is used to query for the relationships that are desired to be deleted. Note that the input type, <code>OrganizationMembersQueryInput</code> is the same input type used to query for relationships under the relationship query in the GraphQL root <code>Query</code> object, described in the section on querying, above.</p>
<pre><code>input OrganizationMembersDeleteInput {
  MATCH: OrganizationMembersQueryInput
  dst: OrganizationMembersDstDeleteMutationInput
  src: OrganizationMembersSrcDeleteMutationInput
}
</code></pre>
<p>The src and destination delete mutation inputs are not particularly interesting for this simple schema. The input type for the src of the relationship contains a single <code>Organization</code> attribute that has the same type as the deletion input for an <code>OrganizationDelete</code> mutation. However, the only option in that type is deletion of members, which is what is already being done. On the destination side, because the <code>User</code> type has no relationships of its own, the <code>UserDeleteMutationInput</code> object is empty altogether. Thus, for the most part, the <code>src</code> and <code>dst</code> attriubtes on the <code>OrganizationMembersDeleteInput</code> are not particularly useful, though in more complex models, they allows the possibility of deleting multiple nodes and relationships in a single query.</p>
<pre><code>input OrganizationMembersSrcDeleteMutationInput {
  Organization: OrganizationDeleteMutationInput
}

input OrganizationMembersDstDeleteMutationInput {
  User: UserDeleteMutationInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}

input UserDeleteMutationInput
</code></pre>
<p>The output from the relationship deletion mutation is an integer with a count of the relationships deleted.</p>
<h3 id="mutating-a-node"><a class="header" href="#mutating-a-node">Mutating a Node</a></h3>
<p>In many ways, modifying a node in a data model that includes relationships is similar to what was described in the node-only portion of the book, previously. Thus, this section doesn't repeat that same content, instead focusing only on the changes the come from having a relationship in the mix.</p>
<h4 id="creating-a-node"><a class="header" href="#creating-a-node">Creating a Node</a></h4>
<p>The snippet below contains the input for creation of an organization. Note the <code>members</code> attribute, of type <code>OrganizationMembersCreateMutationInput</code>, which allows for the creation of members attributes in the same mutation that creates the organization.  The <code>OrganizationMembersCreateMutationInput</code> input type is the same one that is used for the <code>CREATE</code> attribute in the <code>OrganizationMembersCreate</code> mutation under the root GraphQL <code>mutation</code> object. Thus, when creating a node, you can create members for it using the same full flexbility provided by the mutation dedicated to creating relationships. The recursive nature of the creation inputs allows for the creation of entire sub-graphs.</p>
<pre><code>input OrganizationCreateMutationInput {
  id: ID
  members: [OrganizationMembersCreateMutationInput!]
  name: String
}
</code></pre>
<p>The rest of the inputs and output for the node creation mutation are the same as those described previously for a simpler model without relationships.</p>
<h3 id="updating-a-node"><a class="header" href="#updating-a-node">Updating a Node</a></h3>
<p>The <code>OrganizationUpdateInput</code> for making changes to organizations looks similar to the input types used for objects that don't have relationships. It has a <code>MATCH</code> attribute to select the objects to update, and a <code>SET</code> attribute to describe the changes to be made. The difference is in the </p>
<pre><code>input OrganizationUpdateInput {
  MATCH: OrganizationQueryInput
  SET: OrganizationUpdateMutationInput
}

input OrganizationUpdateMutationInput {
  members: [OrganizationMembersChangeInput!]
  name: String
}
</code></pre>
<p>The differences for the inclusion of relationships begin in the <code>OrganizationUpdateMutationInput</code> input type used to set new values for the nodes to be updated, which includes a <code>members</code> attribute of type <code>OrganizationMembersChangeInput</code>. There are three changes one could make to a relationship: add one or more new relationships to destination nodes, delete one or more relationships to destination nodes, or keep the relationships to the same set of destination nodes but make changes to the properties of one or more of those destination nodes.  Those options are captured in the <code>OrganizationMembersChangeInput</code> input type in the schema snippet below.</p>
<pre><code>input OrganizationMembersChangeInput {
  ADD: OrganizationMembersCreateMutationInput
  DELETE: OrganizationMembersDeleteInput
  UPDATE: OrganizationMembersUpdateInput
}
</code></pre>
<p>The <code>OrganizationMembersCreateMutationInput</code> input type for the <code>ADD</code> operation is the same one that was described above as the <code>CREATE</code> attribute the section on mutations to create new relationships. This makes sense, as in this context it is already clear what the source node or nodes are, and the <code>ADD</code> attribute need only create the new relationships to be added. Similarly, the <code>OrganizationMembersDeleteInput</code> used for the <code>DELETE</code> attribute here is the same one that is used for the <code>OrganizationMembersDelete</code> operation under the root GraphQL <code>Mutation</code> type. The match will be scoped to the relationships under the source node(s) selected by the <code>OrganizationUpdateInput</code> <code>MATCH</code> query. As expected, the same is true for the <code>OrganizationMembersUpdateInput</code> input type used for the <code>UPDATE</code> attribute. It's the same as the input used for the <code>OrganizationMembersUpdate</code> mutation under the root GraphQL <code>Mutation</code> type.</p>
<h3 id="deleting-a-node"><a class="header" href="#deleting-a-node">Deleting a Node</a></h3>
<p>The <code>OrganizationDeleteInput</code> input type, shown in the schema snippet below, looks similar to the one for nodes without relationships. However, the <code>OrganizationDeleteMutationInput</code> is different, as it includes a <code>members</code> attribute of type <code>OrganizationMembersDeleteInput</code>, which is the same type used for the <code>OrganizationMembersDelete</code> mutation under the GraphQL root <code>Mutation</code> type. In the case of this model, this additional input does little. In a more complex model with multiple types of relationships, however, it would allow for deletion of whole subgraphs of nodes and relationships.</p>
<pre><code>input OrganizationDeleteInput {
  DELETE: OrganizationDeleteMutationInput
  MATCH: OrganizationQueryInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}
</code></pre>
<h2 id="full-schema-listing"><a class="header" href="#full-schema-listing">Full Schema Listing</a></h2>
<p>The full schema for the example above is included below.</p>
<pre><code>input OrganizationMembersDeleteInput {
  MATCH: OrganizationMembersQueryInput
  dst: OrganizationMembersDstDeleteMutationInput
  src: OrganizationMembersSrcDeleteMutationInput
}

input OrganizationCreateMutationInput {
  id: ID
  members: [OrganizationMembersCreateMutationInput!]
  name: String
}

input OrganizationMembersCreateInput {
  CREATE: [OrganizationMembersCreateMutationInput!]
  MATCH: OrganizationQueryInput
}

input OrganizationMembersSrcQueryInput {
  Organization: OrganizationQueryInput
}

type Mutation {
  OrganizationCreate(
    input: OrganizationCreateMutationInput!
    options: OrganizationOptions
  ): Organization
  OrganizationDelete(input: OrganizationDeleteInput!, options: OrganizationOptions): Int
  OrganizationMembersCreate(
    input: OrganizationMembersCreateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationMembersDelete(
    input: OrganizationMembersDeleteInput!
    options: OrganizationMembersOptions
  ): Int
  OrganizationMembersUpdate(
    input: OrganizationMembersUpdateInput!
    options: OrganizationMembersOptions
  ): [OrganizationMembersRel!]
  OrganizationUpdate(
    input: OrganizationUpdateInput!
    options: OrganizationOptions
  ): [Organization!]
  UserCreate(input: UserCreateMutationInput!, options: UserOptions): User
  UserDelete(input: UserDeleteInput!, options: UserOptions): Int
  UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!]
}

input OrganizationMembersChangeInput {
  ADD: OrganizationMembersCreateMutationInput
  DELETE: OrganizationMembersDeleteInput
  UPDATE: OrganizationMembersUpdateInput
}

input UserUpdateMutationInput {
  email: String
}

input UserDeleteInput {
  DELETE: UserDeleteMutationInput
  MATCH: UserQueryInput
}

input OrganizationMembersNodesMutationInputUnion {
  User: UserInput
}

input UserInput {
  EXISTING: UserQueryInput
  NEW: UserCreateMutationInput
}

input OrganizationQueryInput {
  id: StringQueryInput
  members: OrganizationMembersQueryInput
  name: StringQueryInput
}

union OrganizationMembersNodesUnion = User

type Query {
  Organization(
    input: OrganizationQueryInput
    options: OrganizationOptions
  ): [Organization!]
  OrganizationMembers(
    input: OrganizationMembersQueryInput
    options: OrganizationOptions
  ): [OrganizationMembersRel!]
  User(input: UserQueryInput, options: UserOptions): [User!]
  _version: String
}

input OrganizationMembersDstDeleteMutationInput {
  User: UserDeleteMutationInput
}

input OrganizationMembersUpdateInput {
  MATCH: OrganizationMembersQueryInput
  SET: OrganizationMembersUpdateMutationInput!
}

input OrganizationMembersSrcUpdateMutationInput {
  Organization: OrganizationUpdateMutationInput
}

input UserUpdateInput {
  MATCH: UserQueryInput
  SET: UserUpdateMutationInput
}

input OrganizationUpdateInput {
  MATCH: OrganizationQueryInput
  SET: OrganizationUpdateMutationInput
}

type OrganizationMembersRel {
  dst: OrganizationMembersNodesUnion!
  id: ID!
  joinDate: String
  src: Organization!
}

input OrganizationMembersUpdateMutationInput {
  dst: OrganizationMembersDstUpdateMutationInput
  joinDate: String
  src: OrganizationMembersSrcUpdateMutationInput
}

input OrganizationMembersSrcDeleteMutationInput {
  Organization: OrganizationDeleteMutationInput
}

input UserQueryInput {
  email: StringQueryInput
  id: StringQueryInput
}

input OrganizationMembersQueryInput {
  dst: OrganizationMembersDstQueryInput
  id: StringQueryInput
  joinDate: StringQueryInput
  src: OrganizationMembersSrcQueryInput
}

input OrganizationDeleteMutationInput {
  members: [OrganizationMembersDeleteInput!]
}

type Organization {
  id: ID!
  members(input: OrganizationMembersQueryInput): [OrganizationMembersRel!]
  name: String
}

input OrganizationUpdateMutationInput {
  members: [OrganizationMembersChangeInput!]
  name: String
}

type Subscription

input OrganizationMembersCreateMutationInput {
  dst: OrganizationMembersNodesMutationInputUnion!
  id: ID
  joinDate: String
}

input UserDeleteMutationInput

input OrganizationMembersDstUpdateMutationInput {
  User: UserUpdateMutationInput
}

type User {
  email: String
  id: ID!
}

input OrganizationMembersDstQueryInput {
  User: UserQueryInput
}

input UserCreateMutationInput {
  email: String
  id: ID
}

input StringQueryInput {
  CONTAINS: String
  EQ: String
  GT: String
  GTE: String
  IN: [String!]
  LT: String
  LTE: String
  NOTCONTAINS: String
  NOTEQ: String
  NOTIN: [String!]
}

input OrganizationDeleteInput {
  DELETE: OrganizationDeleteMutationInput
  MATCH: OrganizationQueryInput
}
</code></pre>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../configuration/types.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../api/intro.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../configuration/types.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../api/intro.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>