-
-
Notifications
You must be signed in to change notification settings - Fork 126
Expand file tree
/
Copy pathexamples.json
More file actions
274 lines (274 loc) · 78.2 KB
/
Copy pathexamples.json
File metadata and controls
274 lines (274 loc) · 78.2 KB
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
[
{
"category": "functions",
"title": "Custom queries",
"examples": [
{
"title": "Logged in user field",
"example": "create function viewer()\nreturns users\nas $$\n select *\n from users\n where id = current_user_id();\n /*\n * current_user_id() is a function\n * that returns the logged in user's\n * id, e.g. by extracting from the JWT\n * or indicated via pgSettings.\n */\n$$ language sql stable set search_path from current;\n",
"exampleLanguage": "sql",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -1795,6 +1795,7 @@\n \n \"\"\"Chosen by fair dice roll. Guaranteed to be random. XKCD#221\"\"\"\n randomNumber: Int\n+ viewer: User\n \n \"\"\"Reads a single `Forum` using its globally unique `ID`.\"\"\"\n forumByNodeId(\n",
"resultLanguage": "diff"
}
]
},
{
"category": "functions",
"title": "Computed columns",
"examples": [
{
"title": "User primary email",
"example": "/*\n * Returns the primary email of the\n * current user; for all other users\n * this function will return null.\n */\ncreate function \"users_primaryEmail\"(u users)\nreturns text\nas $$\n select email\n from user_emails\n where user_id = current_user_id()\n and user_id = u.id\n and is_verified is true\n order by id asc\n limit 1;\n$$ language sql stable set search_path from current;\n",
"exampleLanguage": "sql",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -3130,6 +3130,7 @@\n \"\"\"\n condition: QuizEntryCondition\n ): QuizEntriesConnection!\n+ primaryEmail: String\n }\n \n \"\"\"\n",
"resultLanguage": "diff"
}
]
},
{
"category": "functions",
"title": "Custom mutations",
"examples": [
{
"title": "Insert multiple records",
"example": "/**\n * Occasionally you'll want to create a bunch of rows in different tables in a\n * single mutation. Here's an example of how to do that.\n *\n * Pretend we're registering quiz entries, and we want to store each answer in\n * its own table as we want to be able to operate on the answers independently\n * later.\n *\n * This means we want:\n *\n * 1. A mutation that takes input data for inserting one quiz entry and\n * multiple answers.\n * 2. A function that inserts a new quiz entry, inserts an answer for each\n * answer provided in the input data, and connects each answer to the created\n * quiz entry.\n * 3. Finally, we want the function to return the inserted quiz entry itself.\n */\n\n/**\n * This type is used for input in the mutation\n */\ncreate type quiz_entry_input as (\n question text,\n answer int\n);\n\n/**\n * Here's the function that gets turned into a \"custom mutation\"\n */\ncreate function add_quiz_entry(\n quiz_id int,\n answers quiz_entry_input[]\n)\nreturns quiz_entry\nas $$\n declare\n q quiz_entry;\n a quiz_entry_answer;\n begin\n insert into quiz_entry(user_id, quiz_id)\n values(current_user_id(), quiz_id)\n returning * into q;\n\n foreach a in array answers loop\n insert into quiz_entry_answer(quiz_entry_id, question, answer)\n values (quiz_id, a.question, a.answer);\n end loop;\n\n return q;\n end;\n$$ language plpgsql volatile strict set search_path from current;\n",
"exampleLanguage": "sql",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -1,3 +1,41 @@\n+\"\"\"All input for the `addQuizEntry` mutation.\"\"\"\n+input AddQuizEntryInput {\n+ \"\"\"\n+ An arbitrary string value with no semantic meaning. Will be included in the\n+ payload verbatim. May be used to track mutations by the client.\n+ \"\"\"\n+ clientMutationId: String\n+ quizId: Int!\n+ answers: [QuizEntryInputRecordInput]!\n+}\n+\n+\"\"\"The output of our `addQuizEntry` mutation.\"\"\"\n+type AddQuizEntryPayload {\n+ \"\"\"\n+ The exact same `clientMutationId` that was provided in the mutation input,\n+ unchanged and unused. May be used by a client to track mutations.\n+ \"\"\"\n+ clientMutationId: String\n+ quizEntry: QuizEntry\n+\n+ \"\"\"\n+ Our root query field type. Allows us to run any query from our mutation payload.\n+ \"\"\"\n+ query: Query\n+\n+ \"\"\"Reads a single `User` that is related to this `QuizEntry`.\"\"\"\n+ user: User\n+\n+ \"\"\"Reads a single `Quiz` that is related to this `QuizEntry`.\"\"\"\n+ quiz: Quiz\n+\n+ \"\"\"An edge for our `QuizEntry`. May be used by Relay 1.\"\"\"\n+ quizEntryEdge(\n+ \"\"\"The method to use when ordering `QuizEntry`.\"\"\"\n+ orderBy: [QuizEntriesOrderBy!] = [PRIMARY_KEY_ASC]\n+ ): QuizEntriesEdge\n+}\n+\n \"\"\"\n A floating point number that requires more precision than IEEE 754 binary 64\n \"\"\"\n@@ -1472,6 +1510,12 @@\n \"\"\"\n input: DeleteUserByUsernameInput!\n ): DeleteUserPayload\n+ addQuizEntry(\n+ \"\"\"\n+ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n+ \"\"\"\n+ input: AddQuizEntryInput!\n+ ): AddQuizEntryPayload\n \n \"\"\"\n If you've forgotten your password, give us one of your email addresses and we'\n@@ -2115,6 +2159,12 @@\n quizId: Int!\n }\n \n+\"\"\"An input for mutations affecting `QuizEntryInputRecord`\"\"\"\n+input QuizEntryInputRecordInput {\n+ question: String\n+ answer: Int\n+}\n+\n \"\"\"\n Represents an update to a `QuizEntry`. Fields that are set will be updated.\n \"\"\"\n",
"resultLanguage": "diff"
}
]
},
{
"category": "plugins",
"title": "Inflector",
"examples": [
{
"title": "PgRenamePatchToPatchSetPlugin",
"example": "/**\n * Simply renames the `UserPatch` and `PostPatch` type names to be called\n * `UserPatchSet` and `PostPatchSet` instead.\n *\n * Not particularly useful, just an example. ('PatchSet' chosen to minimise\n * diff to make example clearer.)\n *\n * Replaces this inflector:\n * https://github.com/graphile/graphile-engine/blob/f3fb3878692c6959e481e517375da66503428dc5/packages/graphile-build-pg/src/plugins/PgBasicsPlugin.js#L309-L311\n */\nmodule.exports = function PgRenamePatchToPatchSetPlugin(\n builder\n) {\n builder.hook(\n \"inflection\",\n inflector => ({\n // Retain the existing inflectors\n ...inflector,\n\n // Override the patchType inflector\n patchType(typeName) {\n // return this.upperCamelCase(`${typeName}-patch`);\n return this.upperCamelCase(\n `${typeName}-patch-set`\n );\n },\n })\n );\n};\n",
"exampleLanguage": "javascript",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -979,7 +979,7 @@\n \"\"\"\n Represents an update to a `Forum`. Fields that are set will be updated.\n \"\"\"\n-input ForumPatch {\n+input ForumPatchSet {\n id: Int\n \n \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n@@ -1573,7 +1573,7 @@\n }\n \n \"\"\"Represents an update to a `Post`. Fields that are set will be updated.\"\"\"\n-input PostPatch {\n+input PostPatchSet {\n \"\"\"The body of the `Topic`, which Posts reply to.\"\"\"\n body: Html\n }\n@@ -2043,7 +2043,7 @@\n \"\"\"\n Represents an update to a `QuizEntryAnswer`. Fields that are set will be updated.\n \"\"\"\n-input QuizEntryAnswerPatch {\n+input QuizEntryAnswerPatchSet {\n id: Int\n quizEntryId: Int\n question: String\n@@ -2118,7 +2118,7 @@\n \"\"\"\n Represents an update to a `QuizEntry`. Fields that are set will be updated.\n \"\"\"\n-input QuizEntryPatch {\n+input QuizEntryPatchSet {\n id: Int\n userId: Int\n quizId: Int\n@@ -2134,7 +2134,7 @@\n }\n \n \"\"\"Represents an update to a `Quiz`. Fields that are set will be updated.\"\"\"\n-input QuizPatch {\n+input QuizPatchSet {\n id: Int\n name: String\n updatedAt: Datetime\n@@ -2317,7 +2317,7 @@\n \"\"\"\n Represents an update to a `Topic`. Fields that are set will be updated.\n \"\"\"\n-input TopicPatch {\n+input TopicPatchSet {\n id: Int\n forumId: Int\n authorId: Int\n@@ -2394,7 +2394,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Forum` being updated.\n \"\"\"\n- patch: ForumPatch!\n+ patch: ForumPatchSet!\n }\n \n \"\"\"All input for the `updateForumBySlug` mutation.\"\"\"\n@@ -2408,7 +2408,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Forum` being updated.\n \"\"\"\n- patch: ForumPatch!\n+ patch: ForumPatchSet!\n \n \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n slug: String!\n@@ -2425,7 +2425,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Forum` being updated.\n \"\"\"\n- patch: ForumPatch!\n+ patch: ForumPatchSet!\n id: Int!\n }\n \n@@ -2468,7 +2468,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Post` being updated.\n \"\"\"\n- patch: PostPatch!\n+ patch: PostPatchSet!\n }\n \n \"\"\"All input for the `updatePost` mutation.\"\"\"\n@@ -2482,7 +2482,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Post` being updated.\n \"\"\"\n- patch: PostPatch!\n+ patch: PostPatchSet!\n id: Int!\n }\n \n@@ -2531,7 +2531,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Quiz` being updated.\n \"\"\"\n- patch: QuizPatch!\n+ patch: QuizPatchSet!\n }\n \n \"\"\"All input for the `updateQuizEntryAnswerByNodeId` mutation.\"\"\"\n@@ -2550,7 +2550,7 @@\n \"\"\"\n An object where the defined keys will be set on the `QuizEntryAnswer` being updated.\n \"\"\"\n- patch: QuizEntryAnswerPatch!\n+ patch: QuizEntryAnswerPatchSet!\n }\n \n \"\"\"All input for the `updateQuizEntryAnswer` mutation.\"\"\"\n@@ -2564,7 +2564,7 @@\n \"\"\"\n An object where the defined keys will be set on the `QuizEntryAnswer` being updated.\n \"\"\"\n- patch: QuizEntryAnswerPatch!\n+ patch: QuizEntryAnswerPatchSet!\n id: Int!\n }\n \n@@ -2610,7 +2610,7 @@\n \"\"\"\n An object where the defined keys will be set on the `QuizEntry` being updated.\n \"\"\"\n- patch: QuizEntryPatch!\n+ patch: QuizEntryPatchSet!\n }\n \n \"\"\"All input for the `updateQuizEntry` mutation.\"\"\"\n@@ -2624,7 +2624,7 @@\n \"\"\"\n An object where the defined keys will be set on the `QuizEntry` being updated.\n \"\"\"\n- patch: QuizEntryPatch!\n+ patch: QuizEntryPatchSet!\n id: Int!\n }\n \n@@ -2668,7 +2668,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Quiz` being updated.\n \"\"\"\n- patch: QuizPatch!\n+ patch: QuizPatchSet!\n id: Int!\n }\n \n@@ -2711,7 +2711,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Topic` being updated.\n \"\"\"\n- patch: TopicPatch!\n+ patch: TopicPatchSet!\n }\n \n \"\"\"All input for the `updateTopic` mutation.\"\"\"\n@@ -2725,7 +2725,7 @@\n \"\"\"\n An object where the defined keys will be set on the `Topic` being updated.\n \"\"\"\n- patch: TopicPatch!\n+ patch: TopicPatchSet!\n id: Int!\n }\n \n@@ -2774,7 +2774,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserAuthentication` being updated.\n \"\"\"\n- patch: UserAuthenticationPatch!\n+ patch: UserAuthenticationPatchSet!\n }\n \n \"\"\"\n@@ -2790,7 +2790,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserAuthentication` being updated.\n \"\"\"\n- patch: UserAuthenticationPatch!\n+ patch: UserAuthenticationPatchSet!\n \n \"\"\"The login service used, e.g. `twitter` or `github`.\"\"\"\n service: String!\n@@ -2810,7 +2810,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserAuthentication` being updated.\n \"\"\"\n- patch: UserAuthenticationPatch!\n+ patch: UserAuthenticationPatchSet!\n id: Int!\n }\n \n@@ -2853,7 +2853,7 @@\n \"\"\"\n An object where the defined keys will be set on the `User` being updated.\n \"\"\"\n- patch: UserPatch!\n+ patch: UserPatchSet!\n }\n \n \"\"\"All input for the `updateUserByUsername` mutation.\"\"\"\n@@ -2867,7 +2867,7 @@\n \"\"\"\n An object where the defined keys will be set on the `User` being updated.\n \"\"\"\n- patch: UserPatch!\n+ patch: UserPatchSet!\n \n \"\"\"Public-facing username (or 'handle') of the user.\"\"\"\n username: String!\n@@ -2889,7 +2889,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserEmail` being updated.\n \"\"\"\n- patch: UserEmailPatch!\n+ patch: UserEmailPatchSet!\n }\n \n \"\"\"All input for the `updateUserEmailByUserIdAndEmail` mutation.\"\"\"\n@@ -2903,7 +2903,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserEmail` being updated.\n \"\"\"\n- patch: UserEmailPatch!\n+ patch: UserEmailPatchSet!\n userId: Int!\n \n \"\"\"The users email address, in `a@b.c` format.\"\"\"\n@@ -2921,7 +2921,7 @@\n \"\"\"\n An object where the defined keys will be set on the `UserEmail` being updated.\n \"\"\"\n- patch: UserEmailPatch!\n+ patch: UserEmailPatchSet!\n id: Int!\n }\n \n@@ -2962,7 +2962,7 @@\n \"\"\"\n An object where the defined keys will be set on the `User` being updated.\n \"\"\"\n- patch: UserPatch!\n+ patch: UserPatchSet!\n \n \"\"\"Unique identifier for the user.\"\"\"\n id: Int!\n@@ -3167,7 +3167,7 @@\n \"\"\"\n Represents an update to a `UserAuthentication`. Fields that are set will be updated.\n \"\"\"\n-input UserAuthenticationPatch {\n+input UserAuthenticationPatchSet {\n id: Int\n \n \"\"\"The login service used, e.g. `twitter` or `github`.\"\"\"\n@@ -3273,7 +3273,7 @@\n \"\"\"\n Represents an update to a `UserEmail`. Fields that are set will be updated.\n \"\"\"\n-input UserEmailPatch {\n+input UserEmailPatchSet {\n id: Int\n userId: Int\n \n@@ -3355,7 +3355,7 @@\n }\n \n \"\"\"Represents an update to a `User`. Fields that are set will be updated.\"\"\"\n-input UserPatch {\n+input UserPatchSet {\n \"\"\"Unique identifier for the user.\"\"\"\n id: Int\n \n",
"resultLanguage": "diff"
},
{
"title": "PgShortenAllRowsInflectorPlugin",
"example": "/**\n * Simply renames the `allUsers` and `allPosts` Query fields to `users` and\n * `posts` respectively.\n *\n * Not particularly useful, just an example.\n *\n * Replaces this inflector:\n * https://github.com/graphile/graphile-engine/blob/f3fb3878692c6959e481e517375da66503428dc5/packages/graphile-build-pg/src/plugins/PgBasicsPlugin.js#L460-L464\n */\nmodule.exports = function PgShortenAllRowsInflectorPlugin(\n builder\n) {\n builder.hook(\n \"inflection\",\n inflector => ({\n // Retain the existing inflectors\n ...inflector,\n\n // Override the allRows inflector\n allRows(table) {\n return this.camelCase(\n // Was: `all-${this.pluralize(this._singularizedTableName(table))}`\n // Now:\n this.pluralize(\n this._singularizedTableName(\n table\n )\n )\n );\n },\n })\n );\n};\n",
"exampleLanguage": "javascript",
"result": "",
"resultLanguage": "diff"
}
]
},
{
"category": "plugins",
"title": "Types",
"examples": [
{
"title": "PgNumericToFloatPlugin",
"example": "/**\n * Use of this plugin is NOT recommended, please see\n * PgSmallNumericToFloatPlugin for a more appropriate replacement if you need\n * one.\n *\n * This plugin will have PostGraphile use `GraphQLFloat` instead of `BigFloat`\n * for *all* DECIMAL / NUMERIC values, for making PostGraphile v4 slightly more\n * backwards-compatible with v3.\n *\n * It's generally a bad idea to use floating point numbers to represent\n * arbitrary precision numbers such as NUMERIC because loss of precision can\n * occur.\n */\nmodule.exports = function PgNumericToFloatPlugin(\n builder\n) {\n builder.hook(\"build\", build => {\n // Register a type handler for NUMERIC / DECIMAL (oid = 1700), always\n // returning the GraphQLFloat type\n build.pgRegisterGqlTypeByTypeId(\n \"1700\",\n () => build.graphql.GraphQLFloat\n );\n return build;\n });\n};\n",
"exampleLanguage": "javascript",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -1,8 +1,3 @@\n-\"\"\"\n-A floating point number that requires more precision than IEEE 754 binary 64\n-\"\"\"\n-scalar BigFloat\n-\n \"\"\"All input for the create `Forum` mutation.\"\"\"\n input CreateForumInput {\n \"\"\"\n@@ -1863,8 +1858,8 @@\n id: Int!\n name: String!\n updatedAt: Datetime!\n- precision12Scale2: BigFloat\n- precision200Scale100: BigFloat\n+ precision12Scale2: Float\n+ precision200Scale100: Float\n \n \"\"\"Reads and enables pagination through a set of `QuizEntry`.\"\"\"\n quizEntries(\n@@ -1910,10 +1905,10 @@\n updatedAt: Datetime\n \n \"\"\"Checks for equality with the object’s `precision12Scale2` field.\"\"\"\n- precision12Scale2: BigFloat\n+ precision12Scale2: Float\n \n \"\"\"Checks for equality with the object’s `precision200Scale100` field.\"\"\"\n- precision200Scale100: BigFloat\n+ precision200Scale100: Float\n }\n \n \"\"\"A connection to a list of `QuizEntry` values.\"\"\"\n@@ -2129,8 +2124,8 @@\n id: Int\n name: String!\n updatedAt: Datetime\n- precision12Scale2: BigFloat\n- precision200Scale100: BigFloat\n+ precision12Scale2: Float\n+ precision200Scale100: Float\n }\n \n \"\"\"Represents an update to a `Quiz`. Fields that are set will be updated.\"\"\"\n@@ -2138,8 +2133,8 @@\n id: Int\n name: String\n updatedAt: Datetime\n- precision12Scale2: BigFloat\n- precision200Scale100: BigFloat\n+ precision12Scale2: Float\n+ precision200Scale100: Float\n }\n \n \"\"\"A connection to a list of `Quiz` values.\"\"\"\n",
"resultLanguage": "diff"
},
{
"title": "PgSmallNumericToFloatPlugin",
"example": "/**\n * This plugin will have PostGraphile use `GraphQLFloat` instead of `BigFloat`\n * for DECIMAL / NUMERIC values that have a precision and scale under the given\n * limits (currently 12 and 2 respectively).\n *\n * It's generally a bad idea to use floating point numbers to represent\n * arbitrary precision numbers such as NUMERIC because loss of precision can\n * occur; however some systems are okay with this compromise.\n */\nmodule.exports = function PgSmallNumericToFloatPlugin(\n builder,\n {\n pgNumericToFloatPrecisionCap = 12,\n pgNumericToFloatScaleCap = 2,\n }\n) {\n builder.hook(\"build\", build => {\n // Register a type handler for NUMERIC / DECIMAL (oid = 1700)\n build.pgRegisterGqlTypeByTypeId(\n \"1700\",\n (_set, modifier) => {\n if (\n modifier &&\n typeof modifier ===\n \"number\" &&\n modifier > 0\n ) {\n // Ref: https://stackoverflow.com/a/3351120/141284\n const precision =\n ((modifier - 4) >> 16) &\n 65535;\n const scale =\n (modifier - 4) & 65535;\n if (\n precision <=\n pgNumericToFloatPrecisionCap &&\n scale <=\n pgNumericToFloatScaleCap\n ) {\n // This number is no more precise than our cap, so we're declaring\n // that we can handle it as a float:\n return build.graphql\n .GraphQLFloat;\n }\n }\n // If all else fails, let PostGraphile do it's default handling - i.e.\n // BigFloat\n return null;\n }\n );\n\n // We didn't modify _init, but we still must return it.\n return build;\n });\n};\n",
"exampleLanguage": "javascript",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -1863,7 +1863,7 @@\n id: Int!\n name: String!\n updatedAt: Datetime!\n- precision12Scale2: BigFloat\n+ precision12Scale2: Float\n precision200Scale100: BigFloat\n \n \"\"\"Reads and enables pagination through a set of `QuizEntry`.\"\"\"\n@@ -1910,7 +1910,7 @@\n updatedAt: Datetime\n \n \"\"\"Checks for equality with the object’s `precision12Scale2` field.\"\"\"\n- precision12Scale2: BigFloat\n+ precision12Scale2: Float\n \n \"\"\"Checks for equality with the object’s `precision200Scale100` field.\"\"\"\n precision200Scale100: BigFloat\n@@ -2129,7 +2129,7 @@\n id: Int\n name: String!\n updatedAt: Datetime\n- precision12Scale2: BigFloat\n+ precision12Scale2: Float\n precision200Scale100: BigFloat\n }\n \n@@ -2138,7 +2138,7 @@\n id: Int\n name: String\n updatedAt: Datetime\n- precision12Scale2: BigFloat\n+ precision12Scale2: Float\n precision200Scale100: BigFloat\n }\n \n",
"resultLanguage": "diff"
},
{
"title": "SetInputObjectDefaultValue",
"example": "/**\n * This plugin sets a defaultValue on all input object fields that match the\n * given criteria (specifically the 'create' input types, for columns named\n * 'name')\n */\nmodule.exports = function SetInputObjectDefaultValue(\n builder\n) {\n builder.hook(\n \"GraphQLInputObjectType:fields:field\",\n (field, build, context) => {\n const {\n scope: {\n isPgRowType,\n isInputType,\n isPgPatch,\n pgFieldIntrospection: attr,\n },\n } = context;\n if (\n !isPgRowType ||\n !isInputType ||\n isPgPatch ||\n !attr ||\n attr.kind !== \"attribute\" ||\n attr.name !== \"name\"\n ) {\n return field;\n }\n\n return {\n ...field,\n type: build.graphql.getNamedType(\n field.type\n ), // Since it has a default, it can be nullable\n defaultValue:\n // attr.tags.defaultValue: enables overriding this with a\n // `@defaultValue Alice Smith` smart comment\n attr.tags.defaultValue ||\n \"Bobby Tables\",\n };\n }\n );\n};\n",
"exampleLanguage": "javascript",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -968,7 +968,7 @@\n slug: String!\n \n \"\"\"The name of the `Forum` (indicates its subject matter).\"\"\"\n- name: String!\n+ name: String = \"Bobby Tables\"\n \n \"\"\"A brief description of the `Forum` including it's purpose.\"\"\"\n description: String\n@@ -2127,7 +2127,7 @@\n \"\"\"An input for mutations affecting `Quiz`\"\"\"\n input QuizInput {\n id: Int\n- name: String!\n+ name: String = \"Bobby Tables\"\n updatedAt: Datetime\n precision12Scale2: BigFloat\n precision200Scale100: BigFloat\n@@ -3343,7 +3343,7 @@\n username: String!\n \n \"\"\"Public-facing name (or pseudonym) of the user.\"\"\"\n- name: String\n+ name: String = \"Bobby Tables\"\n \n \"\"\"Optional avatar URL.\"\"\"\n avatarUrl: String\n",
"resultLanguage": "diff"
}
]
},
{
"category": "plugins",
"title": "Mutation wrappers",
"examples": [
{
"title": "OverrideArgValuePlugin",
"example": "/**\n * This plugin sets the `input.quizPatch.updatedAt` to the current timestamp in\n * the `updateQuiz*` mutations IFF it's not already set.\n */\nmodule.exports = function SetInputObjectDefaultValue(\n builder\n) {\n builder.hook(\n \"GraphQLObjectType:fields:field\",\n (field, build, context) => {\n const {\n scope: {\n isPgUpdateMutationField,\n pgFieldIntrospection: table,\n },\n } = context;\n if (\n !isPgUpdateMutationField ||\n table.kind !== \"class\" ||\n table.name !== \"quiz\"\n ) {\n return field;\n }\n\n const oldResolve =\n field.resolve;\n\n return {\n ...field,\n resolve(\n _mutation,\n args,\n context,\n info\n ) {\n // Override the `updatedAt` field if it's not already set.\n if (\n args.input.quizPatch\n .updatedAt == null\n ) {\n args.input.quizPatch.updatedAt = new Date().toISOString();\n }\n return oldResolve(\n _mutation,\n args,\n context,\n info\n );\n },\n };\n }\n );\n};\n\n// Tested via:\n// npx postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector,`pwd`/examples/plugins/0300_mutation_wrappers/OverrideArgValuePlugin.js -c graphile_org_demo -s app_public\n",
"exampleLanguage": "javascript",
"result": "",
"resultLanguage": "diff"
}
]
},
{
"category": "plugins",
"title": "Customisation",
"examples": [
{
"title": "OmitMutationsByDefaultPlugin",
"example": "/**\n * This plugin treats any table that doesn't have an `@omit` comment as if it\n * had `@omit create,update,delete` (thereby disabling mutations).\n *\n * Override it by adding a smart comment to the table. To restore all\n * mutations, do `COMMENT ON my_table IS E'@omit :';` (the `:` is special\n * syntax for \"nothing\").\n */\nmodule.exports = function OmitMutationsByDefaultPlugin(\n builder\n) {\n builder.hook(\"build\", build => {\n const {\n pgIntrospectionResultsByKind,\n } = build;\n pgIntrospectionResultsByKind.class\n .filter(\n table =>\n table.isSelectable &&\n table.namespace\n )\n .forEach(table => {\n if (!(\"omit\" in table.tags)) {\n table.tags.omit =\n \"create,update,delete\";\n }\n });\n return build;\n });\n};\n\n// Tested via:\n// npx postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector,`pwd`/examples/plugins/0400_customisation/OmitMutationsByDefaultPlugin.js -c graphile_org_demo -s app_public\n",
"exampleLanguage": "javascript",
"result": "--- Original GraphQL Schema\n+++ Modified GraphQL Schema\n@@ -3,41 +3,6 @@\n \"\"\"\n scalar BigFloat\n \n-\"\"\"All input for the create `Forum` mutation.\"\"\"\n-input CreateForumInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Forum` to be created by this mutation.\"\"\"\n- forum: ForumInput!\n-}\n-\n-\"\"\"The output of our create `Forum` mutation.\"\"\"\n-type CreateForumPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Forum` that was created by this mutation.\"\"\"\n- forum: Forum\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Forum`. May be used by Relay 1.\"\"\"\n- forumEdge(\n- \"\"\"The method to use when ordering `Forum`.\"\"\"\n- orderBy: [ForumsOrderBy!] = [PRIMARY_KEY_ASC]\n- ): ForumsEdge\n-}\n-\n \"\"\"All input for the create `Post` mutation.\"\"\"\n input CreatePostInput {\n \"\"\"\n@@ -79,120 +44,6 @@\n ): PostsEdge\n }\n \n-\"\"\"All input for the create `QuizEntryAnswer` mutation.\"\"\"\n-input CreateQuizEntryAnswerInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntryAnswer` to be created by this mutation.\"\"\"\n- quizEntryAnswer: QuizEntryAnswerInput!\n-}\n-\n-\"\"\"The output of our create `QuizEntryAnswer` mutation.\"\"\"\n-type CreateQuizEntryAnswerPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntryAnswer` that was created by this mutation.\"\"\"\n- quizEntryAnswer: QuizEntryAnswer\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `QuizEntry` that is related to this `QuizEntryAnswer`.\"\"\"\n- quizEntry: QuizEntry\n-\n- \"\"\"An edge for our `QuizEntryAnswer`. May be used by Relay 1.\"\"\"\n- quizEntryAnswerEdge(\n- \"\"\"The method to use when ordering `QuizEntryAnswer`.\"\"\"\n- orderBy: [QuizEntryAnswersOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntryAnswersEdge\n-}\n-\n-\"\"\"All input for the create `QuizEntry` mutation.\"\"\"\n-input CreateQuizEntryInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntry` to be created by this mutation.\"\"\"\n- quizEntry: QuizEntryInput!\n-}\n-\n-\"\"\"The output of our create `QuizEntry` mutation.\"\"\"\n-type CreateQuizEntryPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntry` that was created by this mutation.\"\"\"\n- quizEntry: QuizEntry\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `User` that is related to this `QuizEntry`.\"\"\"\n- user: User\n-\n- \"\"\"Reads a single `Quiz` that is related to this `QuizEntry`.\"\"\"\n- quiz: Quiz\n-\n- \"\"\"An edge for our `QuizEntry`. May be used by Relay 1.\"\"\"\n- quizEntryEdge(\n- \"\"\"The method to use when ordering `QuizEntry`.\"\"\"\n- orderBy: [QuizEntriesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntriesEdge\n-}\n-\n-\"\"\"All input for the create `Quiz` mutation.\"\"\"\n-input CreateQuizInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Quiz` to be created by this mutation.\"\"\"\n- quiz: QuizInput!\n-}\n-\n-\"\"\"The output of our create `Quiz` mutation.\"\"\"\n-type CreateQuizPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Quiz` that was created by this mutation.\"\"\"\n- quiz: Quiz\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Quiz`. May be used by Relay 1.\"\"\"\n- quizEdge(\n- \"\"\"The method to use when ordering `Quiz`.\"\"\"\n- orderBy: [QuizzesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizzesEdge\n-}\n-\n \"\"\"All input for the create `Topic` mutation.\"\"\"\n input CreateTopicInput {\n \"\"\"\n@@ -351,66 +202,6 @@\n \"\"\"\n scalar Datetime\n \n-\"\"\"All input for the `deleteForumByNodeId` mutation.\"\"\"\n-input DeleteForumByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `Forum` to be deleted.\n- \"\"\"\n- nodeId: ID!\n-}\n-\n-\"\"\"All input for the `deleteForumBySlug` mutation.\"\"\"\n-input DeleteForumBySlugInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n- slug: String!\n-}\n-\n-\"\"\"All input for the `deleteForum` mutation.\"\"\"\n-input DeleteForumInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n- id: Int!\n-}\n-\n-\"\"\"The output of our delete `Forum` mutation.\"\"\"\n-type DeleteForumPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Forum` that was deleted by this mutation.\"\"\"\n- forum: Forum\n- deletedForumNodeId: ID\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Forum`. May be used by Relay 1.\"\"\"\n- forumEdge(\n- \"\"\"The method to use when ordering `Forum`.\"\"\"\n- orderBy: [ForumsOrderBy!] = [PRIMARY_KEY_ASC]\n- ): ForumsEdge\n-}\n-\n \"\"\"All input for the `deletePostByNodeId` mutation.\"\"\"\n input DeletePostByNodeIdInput {\n \"\"\"\n@@ -465,159 +256,6 @@\n ): PostsEdge\n }\n \n-\"\"\"All input for the `deleteQuizByNodeId` mutation.\"\"\"\n-input DeleteQuizByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `Quiz` to be deleted.\n- \"\"\"\n- nodeId: ID!\n-}\n-\n-\"\"\"All input for the `deleteQuizEntryAnswerByNodeId` mutation.\"\"\"\n-input DeleteQuizEntryAnswerByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `QuizEntryAnswer` to be deleted.\n- \"\"\"\n- nodeId: ID!\n-}\n-\n-\"\"\"All input for the `deleteQuizEntryAnswer` mutation.\"\"\"\n-input DeleteQuizEntryAnswerInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n- id: Int!\n-}\n-\n-\"\"\"The output of our delete `QuizEntryAnswer` mutation.\"\"\"\n-type DeleteQuizEntryAnswerPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntryAnswer` that was deleted by this mutation.\"\"\"\n- quizEntryAnswer: QuizEntryAnswer\n- deletedQuizEntryAnswerNodeId: ID\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `QuizEntry` that is related to this `QuizEntryAnswer`.\"\"\"\n- quizEntry: QuizEntry\n-\n- \"\"\"An edge for our `QuizEntryAnswer`. May be used by Relay 1.\"\"\"\n- quizEntryAnswerEdge(\n- \"\"\"The method to use when ordering `QuizEntryAnswer`.\"\"\"\n- orderBy: [QuizEntryAnswersOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntryAnswersEdge\n-}\n-\n-\"\"\"All input for the `deleteQuizEntryByNodeId` mutation.\"\"\"\n-input DeleteQuizEntryByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `QuizEntry` to be deleted.\n- \"\"\"\n- nodeId: ID!\n-}\n-\n-\"\"\"All input for the `deleteQuizEntry` mutation.\"\"\"\n-input DeleteQuizEntryInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n- id: Int!\n-}\n-\n-\"\"\"The output of our delete `QuizEntry` mutation.\"\"\"\n-type DeleteQuizEntryPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntry` that was deleted by this mutation.\"\"\"\n- quizEntry: QuizEntry\n- deletedQuizEntryNodeId: ID\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `User` that is related to this `QuizEntry`.\"\"\"\n- user: User\n-\n- \"\"\"Reads a single `Quiz` that is related to this `QuizEntry`.\"\"\"\n- quiz: Quiz\n-\n- \"\"\"An edge for our `QuizEntry`. May be used by Relay 1.\"\"\"\n- quizEntryEdge(\n- \"\"\"The method to use when ordering `QuizEntry`.\"\"\"\n- orderBy: [QuizEntriesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntriesEdge\n-}\n-\n-\"\"\"All input for the `deleteQuiz` mutation.\"\"\"\n-input DeleteQuizInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n- id: Int!\n-}\n-\n-\"\"\"The output of our delete `Quiz` mutation.\"\"\"\n-type DeleteQuizPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Quiz` that was deleted by this mutation.\"\"\"\n- quiz: Quiz\n- deletedQuizNodeId: ID\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Quiz`. May be used by Relay 1.\"\"\"\n- quizEdge(\n- \"\"\"The method to use when ordering `Quiz`.\"\"\"\n- orderBy: [QuizzesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizzesEdge\n-}\n-\n \"\"\"All input for the `deleteTopicByNodeId` mutation.\"\"\"\n input DeleteTopicByNodeIdInput {\n \"\"\"\n@@ -960,40 +598,6 @@\n updatedAt: Datetime\n }\n \n-\"\"\"An input for mutations affecting `Forum`\"\"\"\n-input ForumInput {\n- id: Int\n-\n- \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n- slug: String!\n-\n- \"\"\"The name of the `Forum` (indicates its subject matter).\"\"\"\n- name: String!\n-\n- \"\"\"A brief description of the `Forum` including it's purpose.\"\"\"\n- description: String\n- createdAt: Datetime\n- updatedAt: Datetime\n-}\n-\n-\"\"\"\n-Represents an update to a `Forum`. Fields that are set will be updated.\n-\"\"\"\n-input ForumPatch {\n- id: Int\n-\n- \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n- slug: String\n-\n- \"\"\"The name of the `Forum` (indicates its subject matter).\"\"\"\n- name: String\n-\n- \"\"\"A brief description of the `Forum` including it's purpose.\"\"\"\n- description: String\n- createdAt: Datetime\n- updatedAt: Datetime\n-}\n-\n \"\"\"A connection to a list of `Forum` values.\"\"\"\n type ForumsConnection {\n \"\"\"A list of `Forum` objects.\"\"\"\n@@ -1045,14 +649,6 @@\n The root mutation type which contains root level fields which mutate data.\n \"\"\"\n type Mutation {\n- \"\"\"Creates a single `Forum`.\"\"\"\n- createForum(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: CreateForumInput!\n- ): CreateForumPayload\n-\n \"\"\"Creates a single `Post`.\"\"\"\n createPost(\n \"\"\"\n@@ -1061,30 +657,6 @@\n input: CreatePostInput!\n ): CreatePostPayload\n \n- \"\"\"Creates a single `Quiz`.\"\"\"\n- createQuiz(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: CreateQuizInput!\n- ): CreateQuizPayload\n-\n- \"\"\"Creates a single `QuizEntry`.\"\"\"\n- createQuizEntry(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: CreateQuizEntryInput!\n- ): CreateQuizEntryPayload\n-\n- \"\"\"Creates a single `QuizEntryAnswer`.\"\"\"\n- createQuizEntryAnswer(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: CreateQuizEntryAnswerInput!\n- ): CreateQuizEntryAnswerPayload\n-\n \"\"\"Creates a single `Topic`.\"\"\"\n createTopic(\n \"\"\"\n@@ -1117,30 +689,6 @@\n input: CreateUserInput!\n ): CreateUserPayload\n \n- \"\"\"Updates a single `Forum` using its globally unique id and a patch.\"\"\"\n- updateForumByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateForumByNodeIdInput!\n- ): UpdateForumPayload\n-\n- \"\"\"Updates a single `Forum` using a unique key and a patch.\"\"\"\n- updateForum(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateForumInput!\n- ): UpdateForumPayload\n-\n- \"\"\"Updates a single `Forum` using a unique key and a patch.\"\"\"\n- updateForumBySlug(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateForumBySlugInput!\n- ): UpdateForumPayload\n-\n \"\"\"Updates a single `Post` using its globally unique id and a patch.\"\"\"\n updatePostByNodeId(\n \"\"\"\n@@ -1157,56 +705,6 @@\n input: UpdatePostInput!\n ): UpdatePostPayload\n \n- \"\"\"Updates a single `Quiz` using its globally unique id and a patch.\"\"\"\n- updateQuizByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizByNodeIdInput!\n- ): UpdateQuizPayload\n-\n- \"\"\"Updates a single `Quiz` using a unique key and a patch.\"\"\"\n- updateQuiz(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizInput!\n- ): UpdateQuizPayload\n-\n- \"\"\"Updates a single `QuizEntry` using its globally unique id and a patch.\"\"\"\n- updateQuizEntryByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizEntryByNodeIdInput!\n- ): UpdateQuizEntryPayload\n-\n- \"\"\"Updates a single `QuizEntry` using a unique key and a patch.\"\"\"\n- updateQuizEntry(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizEntryInput!\n- ): UpdateQuizEntryPayload\n-\n- \"\"\"\n- Updates a single `QuizEntryAnswer` using its globally unique id and a patch.\n- \"\"\"\n- updateQuizEntryAnswerByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizEntryAnswerByNodeIdInput!\n- ): UpdateQuizEntryAnswerPayload\n-\n- \"\"\"Updates a single `QuizEntryAnswer` using a unique key and a patch.\"\"\"\n- updateQuizEntryAnswer(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: UpdateQuizEntryAnswerInput!\n- ): UpdateQuizEntryAnswerPayload\n-\n \"\"\"Updates a single `Topic` using its globally unique id and a patch.\"\"\"\n updateTopicByNodeId(\n \"\"\"\n@@ -1297,30 +795,6 @@\n input: UpdateUserByUsernameInput!\n ): UpdateUserPayload\n \n- \"\"\"Deletes a single `Forum` using its globally unique id.\"\"\"\n- deleteForumByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteForumByNodeIdInput!\n- ): DeleteForumPayload\n-\n- \"\"\"Deletes a single `Forum` using a unique key.\"\"\"\n- deleteForum(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteForumInput!\n- ): DeleteForumPayload\n-\n- \"\"\"Deletes a single `Forum` using a unique key.\"\"\"\n- deleteForumBySlug(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteForumBySlugInput!\n- ): DeleteForumPayload\n-\n \"\"\"Deletes a single `Post` using its globally unique id.\"\"\"\n deletePostByNodeId(\n \"\"\"\n@@ -1337,54 +811,6 @@\n input: DeletePostInput!\n ): DeletePostPayload\n \n- \"\"\"Deletes a single `Quiz` using its globally unique id.\"\"\"\n- deleteQuizByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizByNodeIdInput!\n- ): DeleteQuizPayload\n-\n- \"\"\"Deletes a single `Quiz` using a unique key.\"\"\"\n- deleteQuiz(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizInput!\n- ): DeleteQuizPayload\n-\n- \"\"\"Deletes a single `QuizEntry` using its globally unique id.\"\"\"\n- deleteQuizEntryByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizEntryByNodeIdInput!\n- ): DeleteQuizEntryPayload\n-\n- \"\"\"Deletes a single `QuizEntry` using a unique key.\"\"\"\n- deleteQuizEntry(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizEntryInput!\n- ): DeleteQuizEntryPayload\n-\n- \"\"\"Deletes a single `QuizEntryAnswer` using its globally unique id.\"\"\"\n- deleteQuizEntryAnswerByNodeId(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizEntryAnswerByNodeIdInput!\n- ): DeleteQuizEntryAnswerPayload\n-\n- \"\"\"Deletes a single `QuizEntryAnswer` using a unique key.\"\"\"\n- deleteQuizEntryAnswer(\n- \"\"\"\n- The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.\n- \"\"\"\n- input: DeleteQuizEntryAnswerInput!\n- ): DeleteQuizEntryAnswerPayload\n-\n \"\"\"Deletes a single `Topic` using its globally unique id.\"\"\"\n deleteTopicByNodeId(\n \"\"\"\n@@ -2032,24 +1458,6 @@\n answer: Int\n }\n \n-\"\"\"An input for mutations affecting `QuizEntryAnswer`\"\"\"\n-input QuizEntryAnswerInput {\n- id: Int\n- quizEntryId: Int!\n- question: String!\n- answer: Int\n-}\n-\n-\"\"\"\n-Represents an update to a `QuizEntryAnswer`. Fields that are set will be updated.\n-\"\"\"\n-input QuizEntryAnswerPatch {\n- id: Int\n- quizEntryId: Int\n- question: String\n- answer: Int\n-}\n-\n \"\"\"A connection to a list of `QuizEntryAnswer` values.\"\"\"\n type QuizEntryAnswersConnection {\n \"\"\"A list of `QuizEntryAnswer` objects.\"\"\"\n@@ -2108,40 +1516,6 @@\n quizId: Int\n }\n \n-\"\"\"An input for mutations affecting `QuizEntry`\"\"\"\n-input QuizEntryInput {\n- id: Int\n- userId: Int!\n- quizId: Int!\n-}\n-\n-\"\"\"\n-Represents an update to a `QuizEntry`. Fields that are set will be updated.\n-\"\"\"\n-input QuizEntryPatch {\n- id: Int\n- userId: Int\n- quizId: Int\n-}\n-\n-\"\"\"An input for mutations affecting `Quiz`\"\"\"\n-input QuizInput {\n- id: Int\n- name: String!\n- updatedAt: Datetime\n- precision12Scale2: BigFloat\n- precision200Scale100: BigFloat\n-}\n-\n-\"\"\"Represents an update to a `Quiz`. Fields that are set will be updated.\"\"\"\n-input QuizPatch {\n- id: Int\n- name: String\n- updatedAt: Datetime\n- precision12Scale2: BigFloat\n- precision200Scale100: BigFloat\n-}\n-\n \"\"\"A connection to a list of `Quiz` values.\"\"\"\n type QuizzesConnection {\n \"\"\"A list of `Quiz` objects.\"\"\"\n@@ -2378,80 +1752,6 @@\n PRIMARY_KEY_DESC\n }\n \n-\"\"\"All input for the `updateForumByNodeId` mutation.\"\"\"\n-input UpdateForumByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `Forum` to be updated.\n- \"\"\"\n- nodeId: ID!\n-\n- \"\"\"\n- An object where the defined keys will be set on the `Forum` being updated.\n- \"\"\"\n- patch: ForumPatch!\n-}\n-\n-\"\"\"All input for the `updateForumBySlug` mutation.\"\"\"\n-input UpdateForumBySlugInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- An object where the defined keys will be set on the `Forum` being updated.\n- \"\"\"\n- patch: ForumPatch!\n-\n- \"\"\"An URL-safe alias for the `Forum`.\"\"\"\n- slug: String!\n-}\n-\n-\"\"\"All input for the `updateForum` mutation.\"\"\"\n-input UpdateForumInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- An object where the defined keys will be set on the `Forum` being updated.\n- \"\"\"\n- patch: ForumPatch!\n- id: Int!\n-}\n-\n-\"\"\"The output of our update `Forum` mutation.\"\"\"\n-type UpdateForumPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Forum` that was updated by this mutation.\"\"\"\n- forum: Forum\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Forum`. May be used by Relay 1.\"\"\"\n- forumEdge(\n- \"\"\"The method to use when ordering `Forum`.\"\"\"\n- orderBy: [ForumsOrderBy!] = [PRIMARY_KEY_ASC]\n- ): ForumsEdge\n-}\n-\n \"\"\"All input for the `updatePostByNodeId` mutation.\"\"\"\n input UpdatePostByNodeIdInput {\n \"\"\"\n@@ -2515,186 +1815,6 @@\n ): PostsEdge\n }\n \n-\"\"\"All input for the `updateQuizByNodeId` mutation.\"\"\"\n-input UpdateQuizByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `Quiz` to be updated.\n- \"\"\"\n- nodeId: ID!\n-\n- \"\"\"\n- An object where the defined keys will be set on the `Quiz` being updated.\n- \"\"\"\n- patch: QuizPatch!\n-}\n-\n-\"\"\"All input for the `updateQuizEntryAnswerByNodeId` mutation.\"\"\"\n-input UpdateQuizEntryAnswerByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `QuizEntryAnswer` to be updated.\n- \"\"\"\n- nodeId: ID!\n-\n- \"\"\"\n- An object where the defined keys will be set on the `QuizEntryAnswer` being updated.\n- \"\"\"\n- patch: QuizEntryAnswerPatch!\n-}\n-\n-\"\"\"All input for the `updateQuizEntryAnswer` mutation.\"\"\"\n-input UpdateQuizEntryAnswerInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- An object where the defined keys will be set on the `QuizEntryAnswer` being updated.\n- \"\"\"\n- patch: QuizEntryAnswerPatch!\n- id: Int!\n-}\n-\n-\"\"\"The output of our update `QuizEntryAnswer` mutation.\"\"\"\n-type UpdateQuizEntryAnswerPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntryAnswer` that was updated by this mutation.\"\"\"\n- quizEntryAnswer: QuizEntryAnswer\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `QuizEntry` that is related to this `QuizEntryAnswer`.\"\"\"\n- quizEntry: QuizEntry\n-\n- \"\"\"An edge for our `QuizEntryAnswer`. May be used by Relay 1.\"\"\"\n- quizEntryAnswerEdge(\n- \"\"\"The method to use when ordering `QuizEntryAnswer`.\"\"\"\n- orderBy: [QuizEntryAnswersOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntryAnswersEdge\n-}\n-\n-\"\"\"All input for the `updateQuizEntryByNodeId` mutation.\"\"\"\n-input UpdateQuizEntryByNodeIdInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- The globally unique `ID` which will identify a single `QuizEntry` to be updated.\n- \"\"\"\n- nodeId: ID!\n-\n- \"\"\"\n- An object where the defined keys will be set on the `QuizEntry` being updated.\n- \"\"\"\n- patch: QuizEntryPatch!\n-}\n-\n-\"\"\"All input for the `updateQuizEntry` mutation.\"\"\"\n-input UpdateQuizEntryInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- An object where the defined keys will be set on the `QuizEntry` being updated.\n- \"\"\"\n- patch: QuizEntryPatch!\n- id: Int!\n-}\n-\n-\"\"\"The output of our update `QuizEntry` mutation.\"\"\"\n-type UpdateQuizEntryPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `QuizEntry` that was updated by this mutation.\"\"\"\n- quizEntry: QuizEntry\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"Reads a single `User` that is related to this `QuizEntry`.\"\"\"\n- user: User\n-\n- \"\"\"Reads a single `Quiz` that is related to this `QuizEntry`.\"\"\"\n- quiz: Quiz\n-\n- \"\"\"An edge for our `QuizEntry`. May be used by Relay 1.\"\"\"\n- quizEntryEdge(\n- \"\"\"The method to use when ordering `QuizEntry`.\"\"\"\n- orderBy: [QuizEntriesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizEntriesEdge\n-}\n-\n-\"\"\"All input for the `updateQuiz` mutation.\"\"\"\n-input UpdateQuizInput {\n- \"\"\"\n- An arbitrary string value with no semantic meaning. Will be included in the\n- payload verbatim. May be used to track mutations by the client.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"\n- An object where the defined keys will be set on the `Quiz` being updated.\n- \"\"\"\n- patch: QuizPatch!\n- id: Int!\n-}\n-\n-\"\"\"The output of our update `Quiz` mutation.\"\"\"\n-type UpdateQuizPayload {\n- \"\"\"\n- The exact same `clientMutationId` that was provided in the mutation input,\n- unchanged and unused. May be used by a client to track mutations.\n- \"\"\"\n- clientMutationId: String\n-\n- \"\"\"The `Quiz` that was updated by this mutation.\"\"\"\n- quiz: Quiz\n-\n- \"\"\"\n- Our root query field type. Allows us to run any query from our mutation payload.\n- \"\"\"\n- query: Query\n-\n- \"\"\"An edge for our `Quiz`. May be used by Relay 1.\"\"\"\n- quizEdge(\n- \"\"\"The method to use when ordering `Quiz`.\"\"\"\n- orderBy: [QuizzesOrderBy!] = [PRIMARY_KEY_ASC]\n- ): QuizzesEdge\n-}\n-\n \"\"\"All input for the `updateTopicByNodeId` mutation.\"\"\"\n input UpdateTopicByNodeIdInput {\n \"\"\"\n",
"resultLanguage": "diff"
},
{
"title": "SanitizeHTMLTypePlugin",
"example": "// Author: Benjie Gillam\n// License: https://benjie.mit-license.org/\n//\n// This is a documentation example, you will need to edit it to make it useful.\n// Instructions on running this plugin are at the bottom.\n\n// This function is the one that would perform sanitisation (writing actual\n// sanitisation is left as an exercise to the reader)\nfunction sanitize(html) {\n return html.toUpperCase();\n}\n\n// Export our plugin function (it can be async if you want)\nmodule.exports = /* async */ function SanitizeHTMLTypePlugin(\n builder\n) {\n // Builder is an instance of SchemaBuilder:\n //\n // https://www.graphile.org/graphile-build/schema-builder/\n\n //////////////////////////////////////////////////////////////////////////////\n\n // Here we're hooking the init event; this event occurs after the `build`\n // object is finalised, but before we start building our schema - it's the\n // perfect time to hook up additional types.\n //\n // 'init' is an a-typical hook in that the first argument is meaningless (but\n // you should still return it at the end of the hook).\n //\n // Note all hooks in graphile-build must be synchronous; any async work must be done above here.\n builder.hook(\"init\", (_, build) => {\n // The `build` object is an instance of Build: https://www.graphile.org/graphile-build/build-object/\n // graphile-build-pg adds a bunch of additional helpers to this object:\n const {\n pgIntrospectionResultsByKind, // From PgIntrospectionPlugin\n pgRegisterGqlTypeByTypeId, // From PgTypesPlugin\n pgRegisterGqlInputTypeByTypeId, // From PgTypesPlugin\n pg2GqlMapper, // From PgTypesPlugin\n pgSql: sql, // From PgBasicsPlugin, this is equivalent to `require('pg-sql2')` but avoids multiple-module conflicts\n graphql, // Equivalent to `require('graphql')` but avoids multiple-module conflicts\n } = build;\n const { GraphQLString } = graphql;\n\n // First we find the type that we care about. In this case we've done\n //\n // CREATE DOMAIN html AS text;\n // or\n // CREATE DOMAIN public.html AS text;\n //\n // so we are looking for the 'html' type in the 'public' schema (namespace).\n const htmlDomain = pgIntrospectionResultsByKind.type.find(\n type =>\n type.name === \"html\" &&\n type.namespaceName ===\n \"public\"\n );\n\n // If this type exists, then...\n if (htmlDomain) {\n // Register the *output* type for this type, we just want to use the `String` type\n pgRegisterGqlTypeByTypeId(\n htmlDomain.id,\n () => GraphQLString\n );\n\n // Register the *input* type for this type, again we'll use `String`\n pgRegisterGqlInputTypeByTypeId(\n htmlDomain.id,\n () => GraphQLString\n );\n\n // The pg2GqlMapper is responsible for translating things from PostgreSQL\n // into GraphQL and back again.\n pg2GqlMapper[htmlDomain.id] = {\n // From Postgres to GraphQL: we simply take the string from postgres\n // and sanitise it and return the resulting string to GraphQL.\n map: value => sanitize(value),\n\n // From GraphQL to SQL: we must construct an SQL fragment that can be\n // interpolated into larger SQL queries (e.g. as the argument to a\n // function or the input value for a CREATE/UPDATE mutation). Graphile\n // uses the pg-sql2 module for this purpose, you can find the docs\n // here:\n //\n // https://github.com/graphile/pg-sql2/blob/master/README.md\n //\n // We're going to take the value (string) the client gave us, stick it\n // through the sanitise function, then pass it into SQL using\n // `sql.value` to avoid SQL injection and being sure to cast it to our\n // HTML type. Note that if you miss the `sql.value(...)` pg-sql2 will\n // throw an error, so you don't have to worry about accidental SQL\n // injection - just never use `sql.raw`!\n unmap: value =>\n sql.fragment`(${sql.value(\n sanitize(value)\n )}::public.html)`,\n };\n }\n\n // All hooks in graphile-build must return something; normally it's an\n // augmented form of the thing that was passed as the first argument. We\n // don't manipuate _ at all so we can simply return it.\n return _;\n });\n};\n\n/*\n\nYou can test this plugin by saving it to a file 'plugin.js', then executing the\nfollowing:\n\n # Create a database to test against\n createdb sanitise-html\n # Seed the database with our domain, table and some data\n psql -1X sanitise-html <<SQL\n CREATE DOMAIN html AS text;\n CREATE TABLE a (id SERIAL PRIMARY KEY, t TEXT, h HTML);\n INSERT INTO a (t, h) VALUES ('AaAaAa', 'BbBbBb');\n SQL\n # Run PostGraphile\n postgraphile --append-plugins `pwd`/plugin.js -c postgres:///sanitise-html\n\n\nHere's a GraphQL query for selecting the data:\n\n {\n allAs {\n nodes {\n id\n t\n h\n }\n }\n }\n\nAnd one for updating the data:\n\n mutation {\n updateAById(\n input: {\n id: 1\n aPatch: {\n t: \"tttt_TTTT_tttt\"\n h: \"hhhh_HHHH_hhhh\"\n }\n }\n ) {\n a {\n id\n t\n h\n }\n }\n }\n\n*/\n\n// Tested via:\n// npx postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector,`pwd`/examples/plugins/0400_customisation/SanitizeHTMLTypePlugin.js -c graphile_org_demo -s app_public\n",
"exampleLanguage": "javascript",
"result": "",
"resultLanguage": "diff"
}
]
},
{
"category": "plugins",
"title": "Other",
"examples": [
{
"title": "ExtractSmartTagsPlugin",
"example": "/**\n * This plugin will create a file `smartTags.json` containing all of the smart\n * tags gathered from all of the various sources (smart comments, smart tags,\n * plugins, etc etc). This provides a relatively easy migration path from using\n * smart comments to using smart tags instead. Neither Smart Comments nor Smart\n * Tags are \"better\" - they each have trade offs - you can use which ever one\n * matches your teams development flow better (or even mix and match!).\n *\n * Author phryneas (https://github.com/graphile/graphile.github.io/pull/243)\n */\nconst { writeFile } = require(\"fs\");\n\nmodule.exports = builder => {\n builder.hook(\"init\", (_, build) => {\n function sortStuff(a, b) {\n const aSchema =\n \"namespaceName\" in a\n ? a.namespaceName\n : a.class.namespaceName;\n const bSchema =\n \"namespaceName\" in b\n ? b.namespaceName\n : b.class.namespaceName;\n return (\n aSchema.localeCompare(\n bSchema\n ) *\n 100 +\n a.name.localeCompare(b.name)\n );\n }\n const smart = {\n version: 1,\n config: {\n class: [\n ...build\n .pgIntrospectionResultsByKind\n .class,\n ]\n .sort(sortStuff)\n .reduce((acc, pgClass) => {\n let attribute = [\n ...pgClass.attributes,\n ]\n .sort((a, b) =>\n a.name.localeCompare(\n b.name\n )\n )\n .reduce(\n (acc, pgAttr) => {\n const tags =\n Object.keys(\n pgAttr.tags\n ).length > 0\n ? pgAttr.tags\n : undefined;\n if (\n pgAttr.description ||\n tags\n ) {\n acc[\n pgAttr.name\n ] = {\n ...(pgAttr.description\n ? {\n description:\n pgAttr.description,\n }\n : {}),\n tags,\n };\n }\n return acc;\n },\n {}\n );\n if (\n Object.keys(attribute)\n .length === 0\n ) {\n attribute = undefined;\n }\n let constraint = [\n ...pgClass.constraints,\n ]\n .sort(sortStuff)\n .reduce(\n (acc, pgConst) => {\n if (\n pgConst.name.startsWith(\n \"FAKE_\"\n )\n ) {\n return acc;\n }\n const tags =\n Object.keys(\n pgConst.tags\n ).length > 0\n ? pgConst.tags\n : undefined;\n if (\n pgConst.class &&\n (pgConst.description ||\n tags)\n ) {\n acc[\n pgConst.class\n .namespaceName +\n \".\" +\n pgConst.name\n ] = {\n ...(pgConst.description\n ? {\n description:\n pgConst.description,\n }\n : {}),\n tags,\n };\n }\n return acc;\n },\n {}\n );\n if (\n Object.keys(constraint)\n .length === 0\n ) {\n constraint = undefined;\n }\n const tags =\n Object.keys(\n pgClass.tags\n ).length > 0\n ? pgClass.tags\n : undefined;\n if (\n pgClass.description ||\n tags ||\n attribute\n )\n acc[\n pgClass.namespaceName +\n \".\" +\n pgClass.name\n ] = {\n ...(pgClass.description\n ? {\n description:\n pgClass.description,\n }\n : {}),\n tags,\n attribute,\n constraint,\n };\n return acc;\n }, {}),\n procedure: [\n ...build\n .pgIntrospectionResultsByKind\n .procedure,\n ]\n .sort(sortStuff)\n .reduce((acc, pgProc) => {\n if (\n pgProc.name.startsWith(\n \"FAKE_\"\n )\n ) {\n return acc;\n }\n const tags =\n Object.keys(pgProc.tags)\n .length > 0\n ? pgProc.tags\n : undefined;\n if (\n pgProc.description ||\n tags\n ) {\n acc[\n pgProc.namespaceName +\n \".\" +\n pgProc.name\n ] = {\n ...(pgProc.description\n ? {\n description:\n pgProc.description,\n }\n : {}),\n tags,\n };\n }\n return acc;\n }, {}),\n },\n };\n writeFile(\n __dirname + \"/smartTags.json\",\n JSON.stringify(\n smart,\n undefined,\n 2\n ),\n e => {\n console.log(e);\n }\n );\n return _;\n });\n};\n",
"exampleLanguage": "javascript",
"result": "",
"resultLanguage": "diff"
}
]
},
{
"category": "queries",
"title": "Basic",
"examples": [
{
"title": "Forums",
"example": "{\n forums {\n nodes {\n nodeId\n id\n slug\n name\n description\n }\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"forums\": {\n \"nodes\": [\n {\n \"nodeId\": \"WyJmb3J1bXMiLDFd\",\n \"id\": 1,\n \"slug\": \"testimonials\",\n \"name\": \"Testimonials\",\n \"description\": \"How do you rate PostGraphile?\"\n },\n {\n \"nodeId\": \"WyJmb3J1bXMiLDJd\",\n \"id\": 2,\n \"slug\": \"feedback\",\n \"name\": \"Feedback\",\n \"description\": \"How are you finding PostGraphile?\"\n },\n {\n \"nodeId\": \"WyJmb3J1bXMiLDNd\",\n \"id\": 3,\n \"slug\": \"cat-life\",\n \"name\": \"Cat Life\",\n \"description\": \"A forum all about cats and how fluffy they are and how they completely ignore their owners unless there is food. Or yarn.\"\n },\n {\n \"nodeId\": \"WyJmb3J1bXMiLDRd\",\n \"id\": 4,\n \"slug\": \"cat-help\",\n \"name\": \"Cat Help\",\n \"description\": \"A forum to seek advice if your cat is becoming troublesome.\"\n }\n ]\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Forum by slug",
"example": "{\n forumBySlug(slug: \"testimonials\") {\n nodeId\n id\n slug\n name\n description\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"forumBySlug\": {\n \"nodeId\": \"WyJmb3J1bXMiLDFd\",\n \"id\": 1,\n \"slug\": \"testimonials\",\n \"name\": \"Testimonials\",\n \"description\": \"How do you rate PostGraphile?\"\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Collections",
"examples": [
{
"title": "First offset",
"example": "{\n forums(first: 1, offset: 1) {\n nodes {\n nodeId\n id\n name\n }\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"forums\": {\n \"nodes\": [\n {\n \"nodeId\": \"WyJmb3J1bXMiLDJd\",\n \"id\": 2,\n \"name\": \"Feedback\"\n }\n ]\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Relation condition",
"example": "{\n forumBySlug(slug: \"testimonials\") {\n nodeId\n id\n name\n topics(\n condition: { authorId: 2 }\n ) {\n nodes {\n nodeId\n id\n title\n body\n }\n }\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"forumBySlug\": {\n \"nodeId\": \"WyJmb3J1bXMiLDFd\",\n \"id\": 1,\n \"name\": \"Testimonials\",\n \"topics\": {\n \"nodes\": [\n {\n \"nodeId\": \"WyJ0b3BpY3MiLDFd\",\n \"id\": 1,\n \"title\": \"Thank you!\",\n \"body\": \"500-1500 requests per second on a single server is pretty awesome.\"\n }\n ]\n }\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Relations",
"examples": [
{
"title": "Forums topics posts",
"example": "{\n forumBySlug(slug: \"cat-life\") {\n name\n topics(\n first: 1\n orderBy: [CREATED_AT_ASC]\n ) {\n nodes {\n id\n title\n bodySummary\n author {\n id\n username\n }\n posts(\n first: 1\n orderBy: [ID_DESC]\n ) {\n nodes {\n id\n author {\n id\n username\n }\n body\n }\n }\n }\n }\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"forumBySlug\": {\n \"name\": \"Cat Life\",\n \"topics\": {\n \"nodes\": [\n {\n \"id\": 4,\n \"title\": \"I love cats!\",\n \"bodySummary\": \"They're the best!\",\n \"author\": {\n \"id\": 1,\n \"username\": \"user\"\n },\n \"posts\": {\n \"nodes\": [\n {\n \"id\": 6,\n \"author\": {\n \"id\": 3,\n \"username\": \"Bradley_A\"\n },\n \"body\": \"I love it when they completely ignore you until they want something. So much better than dogs am I rite?\"\n }\n ]\n }\n }\n ]\n }\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Mutations",
"examples": [
{
"title": "Create",
"example": "mutation {\n createTopic(\n input: {\n topic: {\n forumId: 2\n title: \"My question relates to mutations...\"\n body: \"How do you write them?\"\n }\n }\n ) {\n topic {\n nodeId\n id\n forumId\n title\n body\n }\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"createTopic\": {\n \"topic\": {\n \"nodeId\": \"WyJ0b3BpY3MiLDVd\",\n \"id\": 5,\n \"forumId\": 2,\n \"title\": \"My question relates to mutations...\",\n \"body\": \"How do you write them?\"\n }\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Update",
"example": "mutation {\n updateTopic(\n input: {\n id: 1\n patch: {\n title: \"My (edited) title\"\n }\n }\n ) {\n topic {\n nodeId\n id\n title\n body\n }\n }\n}\n\n# Works for a table like:\n#\n# create table app_public.topics (\n# id serial primary key,\n# forum_id integer NOT NULL references app_public.forums on delete cascade,\n# title text NOT NULL,\n# body text DEFAULT ''::text NOT NULL\n# );\n",
"exampleLanguage": "graphql",
"result": "{\n \"updateTopic\": {\n \"topic\": {\n \"nodeId\": \"WyJ0b3BpY3MiLDFd\",\n \"id\": 1,\n \"title\": \"My (edited) title\",\n \"body\": \"500-1500 requests per second on a single server is pretty awesome.\"\n }\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Delete",
"example": "mutation {\n deleteTopic(input: { id: 1 }) {\n deletedTopicNodeId\n }\n}\n",
"exampleLanguage": "graphql",
"result": "{\n \"deleteTopic\": {\n \"deletedTopicNodeId\": \"WyJ0b3BpY3MiLDFd\"\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Custom queries",
"examples": [
{
"title": "Single scalar",
"example": "{\n randomNumber\n}\n\n# Generated by SQL like:\n#\n# create function app_public.random_number() returns int\n# language sql stable\n# as $$\n# select 4; -- Chosen by fair dice roll. Guaranteed to be random. XKCD#221\n# $$;\n#\n",
"exampleLanguage": "graphql",
"result": "{ \"randomNumber\": 4 }\n",
"resultLanguage": "json"
},
{
"title": "Single row",
"example": "{\n currentUser {\n nodeId\n id\n username\n }\n}\n\n# Added to the GraphQL schema via\n# this SQL:\n#\n# create function current_user()\n# returns app_public.users\n# language sql stable\n# as $$\n# select users.*\n# from app_public.users\n# where id = current_user_id();\n# $$;\n",
"exampleLanguage": "graphql",
"result": "{\n \"currentUser\": {\n \"nodeId\": \"WyJ1c2VycyIsMV0=\",\n \"id\": 1,\n \"username\": \"user\"\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Rows connection",
"example": "{\n forumsAboutCats {\n nodes {\n nodeId\n id\n name\n slug\n }\n }\n}\n\n# Created from SQL like:\n#\n# create function app_public.forums_about_cats()\n# returns setof app_public.forums\n# language sql stable\n# as $$\n# select *\n# from app_public.forums\n# where slug like 'cat-%';\n# $$;\n",
"exampleLanguage": "graphql",
"result": "{\n \"forumsAboutCats\": {\n \"nodes\": [\n {\n \"nodeId\": \"WyJmb3J1bXMiLDNd\",\n \"id\": 3,\n \"name\": \"Cat Life\",\n \"slug\": \"cat-life\"\n },\n {\n \"nodeId\": \"WyJmb3J1bXMiLDRd\",\n \"id\": 4,\n \"name\": \"Cat Help\",\n \"slug\": \"cat-help\"\n }\n ]\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Custom mutations",
"examples": [
{
"title": "Forgot password",
"example": "mutation {\n forgotPassword(\n input: {\n email: \"benjie@example.com\"\n }\n ) {\n success\n }\n}\n\n# Generated with SQL like this:\n#\n# create function forgot_password(email text)\n# returns boolean\n# language plpgsql volatile\n# as $$\n# ...\n# $$;\n#\n# -- Optionally rename the result field:\n# comment on function\n# forgot_password(email text)\n# is '@resultFieldName success';\n",
"exampleLanguage": "graphql",
"result": "{\n \"forgotPassword\": {\n \"success\": true\n }\n}\n",
"resultLanguage": "json"
}
]
},
{
"category": "queries",
"title": "Computed columns",
"examples": [
{
"title": "Topic summary",
"example": "{\n topic(id: 2) {\n body\n bodySummary\n }\n}\n\n# Generated by SQL like:\n#\n# create function app_public.topics_body_summary(\n# t app_public.topics,\n# max_length int = 30\n# )\n# returns text\n# language sql stable\n# as $$\n# select case\n# when length(t.body) > max_length\n# then left(t.body, max_length - 3)\n# || '...'\n# else t.body\n# end;\n# $$;\n",
"exampleLanguage": "graphql",
"result": "{\n \"topic\": {\n \"body\": \"PostGraphile is a powerful, idomatic, and elegant tool.\",\n \"bodySummary\": \"PostGraphile is a powerful,...\"\n }\n}\n",
"resultLanguage": "json"
},
{
"title": "Topic summary with arg",
"example": "{\n topic(id: 2) {\n body\n bodySummary(maxLength: 20)\n }\n}\n\n# Generated by SQL like:\n#\n# create function app_public.topics_body_summary(\n# t app_public.topics,\n# max_length int = 30\n# )\n# returns text\n# language sql stable\n# as $$\n# select case\n# when length(t.body) > max_length\n# then left(t.body, max_length - 3)\n# || '...'\n# else t.body\n# end;\n# $$;\n",
"exampleLanguage": "graphql",
"result": "{\n \"topic\": {\n \"body\": \"PostGraphile is a powerful, idomatic, and elegant tool.\",\n \"bodySummary\": \"PostGraphile is a...\"\n }\n}\n",
"resultLanguage": "json"
}
]
}
]