@constructive-io/graphql-query 3.2.4 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +411 -65
- package/ast.d.ts +4 -4
- package/ast.js +24 -9
- package/client/error.d.ts +95 -0
- package/client/error.js +277 -0
- package/client/execute.d.ts +57 -0
- package/client/execute.js +124 -0
- package/client/index.d.ts +8 -0
- package/client/index.js +20 -0
- package/client/typed-document.d.ts +31 -0
- package/client/typed-document.js +44 -0
- package/custom-ast.d.ts +22 -8
- package/custom-ast.js +16 -1
- package/esm/ast.js +22 -7
- package/esm/client/error.js +271 -0
- package/esm/client/execute.js +120 -0
- package/esm/client/index.js +8 -0
- package/esm/client/typed-document.js +40 -0
- package/esm/custom-ast.js +16 -1
- package/esm/generators/field-selector.js +381 -0
- package/esm/generators/index.js +13 -0
- package/esm/generators/mutations.js +200 -0
- package/esm/generators/naming-helpers.js +154 -0
- package/esm/generators/select.js +661 -0
- package/esm/index.js +30 -0
- package/esm/introspect/index.js +9 -0
- package/esm/introspect/infer-tables.js +697 -0
- package/esm/introspect/schema-query.js +120 -0
- package/esm/introspect/transform-schema.js +271 -0
- package/esm/introspect/transform.js +38 -0
- package/esm/meta-object/convert.js +3 -0
- package/esm/meta-object/format.json +11 -41
- package/esm/meta-object/validate.js +20 -4
- package/esm/query-builder.js +14 -18
- package/esm/types/index.js +18 -0
- package/esm/types/introspection.js +54 -0
- package/esm/types/mutation.js +4 -0
- package/esm/types/query.js +4 -0
- package/esm/types/schema.js +5 -0
- package/esm/types/selection.js +4 -0
- package/esm/utils.js +69 -0
- package/generators/field-selector.d.ts +30 -0
- package/generators/field-selector.js +387 -0
- package/generators/index.d.ts +9 -0
- package/generators/index.js +42 -0
- package/generators/mutations.d.ts +30 -0
- package/generators/mutations.js +238 -0
- package/generators/naming-helpers.d.ts +48 -0
- package/generators/naming-helpers.js +169 -0
- package/generators/select.d.ts +39 -0
- package/generators/select.js +705 -0
- package/index.d.ts +19 -0
- package/index.js +34 -1
- package/introspect/index.d.ts +9 -0
- package/introspect/index.js +25 -0
- package/introspect/infer-tables.d.ts +42 -0
- package/introspect/infer-tables.js +700 -0
- package/introspect/schema-query.d.ts +20 -0
- package/introspect/schema-query.js +123 -0
- package/introspect/transform-schema.d.ts +86 -0
- package/introspect/transform-schema.js +281 -0
- package/introspect/transform.d.ts +20 -0
- package/introspect/transform.js +43 -0
- package/meta-object/convert.d.ts +3 -0
- package/meta-object/convert.js +3 -0
- package/meta-object/format.json +11 -41
- package/meta-object/validate.d.ts +8 -3
- package/meta-object/validate.js +20 -4
- package/package.json +4 -3
- package/query-builder.d.ts +11 -12
- package/query-builder.js +25 -29
- package/{types.d.ts → types/core.d.ts} +25 -18
- package/types/index.d.ts +12 -0
- package/types/index.js +34 -0
- package/types/introspection.d.ts +121 -0
- package/types/introspection.js +62 -0
- package/types/mutation.d.ts +45 -0
- package/types/mutation.js +5 -0
- package/types/query.d.ts +91 -0
- package/types/query.js +5 -0
- package/types/schema.d.ts +265 -0
- package/types/schema.js +6 -0
- package/types/selection.d.ts +43 -0
- package/types/selection.js +5 -0
- package/utils.d.ts +17 -0
- package/utils.js +72 -0
- /package/esm/{types.js → types/core.js} +0 -0
- /package/{types.js → types/core.js} +0 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<a href="https://www.npmjs.com/package/@constructive-io/graphql-query"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphql%2Fquery%2Fpackage.json"/></a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
>
|
|
15
|
+
> Browser-safe GraphQL query generation core for PostGraphile schemas. Build type-safe queries, mutations, and introspection pipelines — at runtime or build time.
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -20,68 +20,71 @@
|
|
|
20
20
|
npm install @constructive-io/graphql-query
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Overview
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
* ✅ Schema-aware via introspection (PostGraphile optimized)
|
|
27
|
-
* 🧠 Prevents common query syntax issues
|
|
28
|
-
* 🧩 Designed for composability and clean syntax
|
|
25
|
+
This package is the **canonical source** for PostGraphile query generation logic. It provides:
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
- **Query generators** — `buildSelect`, `buildFindOne`, `buildCount` for read operations
|
|
28
|
+
- **Mutation generators** — `buildPostGraphileCreate`, `buildPostGraphileUpdate`, `buildPostGraphileDelete`
|
|
29
|
+
- **Introspection pipeline** — `inferTablesFromIntrospection` to convert a GraphQL schema into `CleanTable` metadata
|
|
30
|
+
- **AST builders** — low-level `getAll`, `getMany`, `getOne`, `createOne`, `patchOne`, `deleteOne`
|
|
31
|
+
- **Client utilities** — `TypedDocumentString`, `execute`, `DataError` for type-safe execution and error handling
|
|
32
|
+
- **Naming helpers** — server-aware inflection functions that respect PostGraphile's schema naming
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
All modules are **browser-safe** (no Node.js APIs). `@constructive-io/graphql-codegen` depends on this package for the core logic and adds Node.js-only features (CLI, file output, watch mode).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start: Generate Queries from a Schema
|
|
39
|
+
|
|
40
|
+
The most common workflow: introspect a GraphQL schema, then generate queries and mutations for any table.
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
### Step 1 — Introspect
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import {
|
|
46
|
+
inferTablesFromIntrospection,
|
|
47
|
+
SCHEMA_INTROSPECTION_QUERY,
|
|
48
|
+
} from '@constructive-io/graphql-query';
|
|
49
|
+
|
|
50
|
+
// Fetch introspection from any GraphQL endpoint
|
|
51
|
+
const response = await fetch('/graphql', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify({ query: SCHEMA_INTROSPECTION_QUERY }),
|
|
37
55
|
});
|
|
56
|
+
const { data } = await response.json();
|
|
38
57
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.getMany({
|
|
43
|
-
select: {
|
|
44
|
-
id: true,
|
|
45
|
-
name: true,
|
|
46
|
-
photo: true,
|
|
47
|
-
title: true,
|
|
48
|
-
actionResults: {
|
|
49
|
-
select: {
|
|
50
|
-
id: true,
|
|
51
|
-
actionId: true
|
|
52
|
-
},
|
|
53
|
-
variables: {
|
|
54
|
-
first: 10,
|
|
55
|
-
before: null,
|
|
56
|
-
filter: {
|
|
57
|
-
name: {
|
|
58
|
-
in: ['abc', 'def']
|
|
59
|
-
},
|
|
60
|
-
actionId: {
|
|
61
|
-
equalTo: 'dc310161-7a42-4b93-6a56-9fa48adcad7e'
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
.print();
|
|
58
|
+
// Convert to CleanTable metadata
|
|
59
|
+
const tables = inferTablesFromIntrospection(data);
|
|
60
|
+
// tables = [{ name: 'User', fields: [...], relations: {...}, query: {...}, inflection: {...} }, ...]
|
|
69
61
|
```
|
|
70
62
|
|
|
71
|
-
|
|
63
|
+
### Step 2 — Generate a SELECT query
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { buildSelect } from '@constructive-io/graphql-query';
|
|
67
|
+
|
|
68
|
+
const userTable = tables.find(t => t.name === 'User')!;
|
|
69
|
+
const query = buildSelect(userTable, tables);
|
|
70
|
+
|
|
71
|
+
console.log(query.toString());
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Generated GraphQL:**
|
|
72
75
|
|
|
73
76
|
```graphql
|
|
74
|
-
query
|
|
77
|
+
query getUsersQuery(
|
|
75
78
|
$first: Int
|
|
76
79
|
$last: Int
|
|
77
80
|
$after: Cursor
|
|
78
81
|
$before: Cursor
|
|
79
82
|
$offset: Int
|
|
80
|
-
$condition:
|
|
81
|
-
$filter:
|
|
82
|
-
$orderBy: [
|
|
83
|
+
$condition: UserCondition
|
|
84
|
+
$filter: UserFilter
|
|
85
|
+
$orderBy: [UsersOrderBy!]
|
|
83
86
|
) {
|
|
84
|
-
|
|
87
|
+
users(
|
|
85
88
|
first: $first
|
|
86
89
|
last: $last
|
|
87
90
|
offset: $offset
|
|
@@ -98,32 +101,375 @@ query getActionsQuery(
|
|
|
98
101
|
endCursor
|
|
99
102
|
startCursor
|
|
100
103
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
nodes {
|
|
105
|
+
id
|
|
106
|
+
name
|
|
107
|
+
email
|
|
108
|
+
createdAt
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Step 3 — Generate mutations
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import {
|
|
118
|
+
buildPostGraphileCreate,
|
|
119
|
+
buildPostGraphileUpdate,
|
|
120
|
+
buildPostGraphileDelete,
|
|
121
|
+
} from '@constructive-io/graphql-query';
|
|
122
|
+
|
|
123
|
+
const createQuery = buildPostGraphileCreate(userTable, tables);
|
|
124
|
+
const updateQuery = buildPostGraphileUpdate(userTable, tables);
|
|
125
|
+
const deleteQuery = buildPostGraphileDelete(userTable, tables);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Generated CREATE mutation:**
|
|
129
|
+
|
|
130
|
+
```graphql
|
|
131
|
+
mutation createUserMutation($input: CreateUserInput!) {
|
|
132
|
+
createUser(input: $input) {
|
|
133
|
+
user {
|
|
134
|
+
id
|
|
135
|
+
name
|
|
136
|
+
email
|
|
137
|
+
createdAt
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Generated UPDATE mutation:**
|
|
144
|
+
|
|
145
|
+
```graphql
|
|
146
|
+
mutation updateUserMutation($input: UpdateUserInput!) {
|
|
147
|
+
updateUser(input: $input) {
|
|
148
|
+
user {
|
|
149
|
+
id
|
|
150
|
+
name
|
|
151
|
+
email
|
|
152
|
+
createdAt
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Generated DELETE mutation:**
|
|
159
|
+
|
|
160
|
+
```graphql
|
|
161
|
+
mutation deleteUserMutation($input: DeleteUserInput!) {
|
|
162
|
+
deleteUser(input: $input) {
|
|
163
|
+
clientMutationId
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Nested Relations
|
|
171
|
+
|
|
172
|
+
Include related tables in your query with automatic Connection wrapping for hasMany relations:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { buildSelect } from '@constructive-io/graphql-query';
|
|
176
|
+
|
|
177
|
+
const actionTable = tables.find(t => t.name === 'Action')!;
|
|
178
|
+
|
|
179
|
+
const query = buildSelect(actionTable, tables, {
|
|
180
|
+
fieldSelection: {
|
|
181
|
+
select: ['id', 'name', 'photo', 'title'],
|
|
182
|
+
include: {
|
|
183
|
+
actionResults: ['id', 'actionId'], // hasMany → wrapped in nodes { ... }
|
|
184
|
+
category: true, // belongsTo → direct nesting
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Generated GraphQL:**
|
|
191
|
+
|
|
192
|
+
```graphql
|
|
193
|
+
query actionsQuery {
|
|
194
|
+
actions {
|
|
195
|
+
totalCount
|
|
196
|
+
nodes {
|
|
197
|
+
id
|
|
198
|
+
name
|
|
199
|
+
photo
|
|
200
|
+
title
|
|
201
|
+
actionResults(first: 20) {
|
|
202
|
+
nodes {
|
|
203
|
+
id
|
|
204
|
+
actionId
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
category {
|
|
104
208
|
id
|
|
105
209
|
name
|
|
106
|
-
photo
|
|
107
|
-
title
|
|
108
|
-
actionResults(
|
|
109
|
-
first: 10
|
|
110
|
-
before: null
|
|
111
|
-
filter: {
|
|
112
|
-
name: { in: ["abc", "def"] }
|
|
113
|
-
actionId: { equalTo: "dc310161-7a42-4b93-6a56-9fa48adcad7e" }
|
|
114
|
-
}
|
|
115
|
-
) {
|
|
116
|
-
nodes {
|
|
117
|
-
id
|
|
118
|
-
actionId
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
210
|
}
|
|
122
211
|
}
|
|
123
212
|
}
|
|
124
213
|
}
|
|
125
214
|
```
|
|
126
215
|
|
|
216
|
+
> **hasMany** relations are automatically wrapped in the PostGraphile Connection pattern (`nodes { ... }` with a default `first: 20` limit).
|
|
217
|
+
> **belongsTo** relations are nested directly.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## FindOne Query
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { buildFindOne } from '@constructive-io/graphql-query';
|
|
225
|
+
|
|
226
|
+
const findOneQuery = buildFindOne(userTable);
|
|
227
|
+
console.log(findOneQuery.toString());
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Generated GraphQL:**
|
|
231
|
+
|
|
232
|
+
```graphql
|
|
233
|
+
query getUserQuery($id: UUID!) {
|
|
234
|
+
user(id: $id) {
|
|
235
|
+
id
|
|
236
|
+
name
|
|
237
|
+
email
|
|
238
|
+
createdAt
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Count Query
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import { buildCount } from '@constructive-io/graphql-query';
|
|
249
|
+
|
|
250
|
+
const countQuery = buildCount(userTable);
|
|
251
|
+
console.log(countQuery.toString());
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Generated GraphQL:**
|
|
255
|
+
|
|
256
|
+
```graphql
|
|
257
|
+
query getUsersCountQuery(
|
|
258
|
+
$condition: UserCondition
|
|
259
|
+
$filter: UserFilter
|
|
260
|
+
) {
|
|
261
|
+
users(condition: $condition, filter: $filter) {
|
|
262
|
+
totalCount
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Field Selection
|
|
270
|
+
|
|
271
|
+
Control which fields and relations are included using presets or custom selection:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { buildSelect } from '@constructive-io/graphql-query';
|
|
275
|
+
|
|
276
|
+
// Preset: just id + a few display fields
|
|
277
|
+
const minimal = buildSelect(userTable, tables, { fieldSelection: 'minimal' });
|
|
278
|
+
|
|
279
|
+
// Preset: all scalar fields (no relations)
|
|
280
|
+
const allFields = buildSelect(userTable, tables, { fieldSelection: 'all' });
|
|
281
|
+
|
|
282
|
+
// Preset: everything including relations
|
|
283
|
+
const full = buildSelect(userTable, tables, { fieldSelection: 'full' });
|
|
284
|
+
|
|
285
|
+
// Custom: pick specific fields + exclude some + include specific relations
|
|
286
|
+
const custom = buildSelect(userTable, tables, {
|
|
287
|
+
fieldSelection: {
|
|
288
|
+
select: ['id', 'name', 'email'],
|
|
289
|
+
exclude: ['internalNotes'],
|
|
290
|
+
include: { posts: ['id', 'title'] },
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Relation Field Mapping (Aliases)
|
|
298
|
+
|
|
299
|
+
Remap relation field names when the server-side name differs from what your consumer expects:
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
const query = buildSelect(userTable, tables, {
|
|
303
|
+
relationFieldMap: {
|
|
304
|
+
contact: 'contactByOwnerId', // emits: contact: contactByOwnerId { ... }
|
|
305
|
+
internalNotes: null, // omits this relation entirely
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Type-Safe Execution
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { createGraphQLClient } from '@constructive-io/graphql-query';
|
|
316
|
+
|
|
317
|
+
const client = createGraphQLClient({
|
|
318
|
+
url: '/graphql',
|
|
319
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const { data, errors } = await client.execute(query, {
|
|
323
|
+
first: 10,
|
|
324
|
+
filter: { name: { includesInsensitive: 'search' } },
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Error Handling
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { DataError, parseGraphQLError, DataErrorType } from '@constructive-io/graphql-query';
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const result = await client.execute(createQuery, { input: { user: { name: 'Alice' } } });
|
|
337
|
+
if (result.errors) {
|
|
338
|
+
const error = parseGraphQLError(result.errors[0]);
|
|
339
|
+
if (error.type === DataErrorType.UNIQUE_VIOLATION) {
|
|
340
|
+
console.log('Duplicate entry:', error.constraintName);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (err instanceof DataError) {
|
|
345
|
+
console.log(err.type, err.message);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Server-Aware Naming
|
|
353
|
+
|
|
354
|
+
All generators automatically use server-inferred names from introspection when available, falling back to local inflection conventions:
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
import {
|
|
358
|
+
toCamelCaseSingular,
|
|
359
|
+
toCamelCasePlural,
|
|
360
|
+
toCreateMutationName,
|
|
361
|
+
toPatchFieldName,
|
|
362
|
+
toFilterTypeName,
|
|
363
|
+
} from '@constructive-io/graphql-query';
|
|
364
|
+
|
|
365
|
+
// Uses table.query/table.inflection if available, falls back to convention
|
|
366
|
+
toCamelCaseSingular('DeliveryZone', table); // "deliveryZone" (from table.inflection.tableFieldName)
|
|
367
|
+
toCamelCasePlural('DeliveryZone', table); // "deliveryZones" (from table.query.all)
|
|
368
|
+
toCreateMutationName('User', table); // "createUser" (from table.query.create)
|
|
369
|
+
toPatchFieldName('User', table); // "userPatch" (from table.query.patchFieldName)
|
|
370
|
+
toFilterTypeName('User', table); // "UserFilter" (from table.inflection.filterType)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Architecture
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
GraphQL Schema (introspection or _meta)
|
|
379
|
+
|
|
|
380
|
+
v
|
|
381
|
+
inferTablesFromIntrospection()
|
|
382
|
+
|
|
|
383
|
+
v
|
|
384
|
+
CleanTable[] (normalized metadata with inflection + query names)
|
|
385
|
+
|
|
|
386
|
+
v
|
|
387
|
+
buildSelect / buildFindOne / buildCount / buildPostGraphileCreate / ...
|
|
388
|
+
|
|
|
389
|
+
v
|
|
390
|
+
gql-ast (AST node factories)
|
|
391
|
+
|
|
|
392
|
+
v
|
|
393
|
+
graphql print() -> query string
|
|
394
|
+
|
|
|
395
|
+
v
|
|
396
|
+
TypedDocumentString (type-safe wrapper)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Package Relationship
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
@constructive-io/graphql-query (this package — browser-safe core)
|
|
403
|
+
|
|
|
404
|
+
v
|
|
405
|
+
@constructive-io/graphql-codegen (Node.js CLI — depends on graphql-query)
|
|
406
|
+
+ CLI entry points
|
|
407
|
+
+ File output (writes .ts files to disk)
|
|
408
|
+
+ Watch mode
|
|
409
|
+
+ Database introspection
|
|
410
|
+
+ React Query hook generation
|
|
411
|
+
+ Babel codegen templates
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## API Reference
|
|
417
|
+
|
|
418
|
+
### Generators
|
|
419
|
+
|
|
420
|
+
| Function | Description |
|
|
421
|
+
|---|---|
|
|
422
|
+
| `buildSelect(table, allTables, options?)` | Build a paginated SELECT query with filters, sorting, field selection |
|
|
423
|
+
| `buildFindOne(table, pkField?)` | Build a single-row query by primary key |
|
|
424
|
+
| `buildCount(table)` | Build a count query with optional condition/filter |
|
|
425
|
+
| `buildPostGraphileCreate(table, allTables, options?)` | Build a CREATE mutation |
|
|
426
|
+
| `buildPostGraphileUpdate(table, allTables, options?)` | Build an UPDATE mutation |
|
|
427
|
+
| `buildPostGraphileDelete(table, allTables, options?)` | Build a DELETE mutation |
|
|
428
|
+
|
|
429
|
+
### Introspection
|
|
430
|
+
|
|
431
|
+
| Function | Description |
|
|
432
|
+
|---|---|
|
|
433
|
+
| `inferTablesFromIntrospection(data, options?)` | Convert GraphQL introspection to `CleanTable[]` |
|
|
434
|
+
| `transformSchemaToOperations(types, queryType, mutationType)` | Extract operations from schema types |
|
|
435
|
+
| `SCHEMA_INTROSPECTION_QUERY` | Full introspection query string |
|
|
436
|
+
|
|
437
|
+
### Client
|
|
438
|
+
|
|
439
|
+
| Export | Description |
|
|
440
|
+
|---|---|
|
|
441
|
+
| `TypedDocumentString` | Type-safe GraphQL document wrapper (compatible with codegen client) |
|
|
442
|
+
| `createGraphQLClient(options)` | Create a typed GraphQL client |
|
|
443
|
+
| `execute(url, query, variables, options?)` | Execute a GraphQL query |
|
|
444
|
+
| `DataError` | Structured error class with PG SQLSTATE classification |
|
|
445
|
+
| `parseGraphQLError(error)` | Parse raw GraphQL error into `DataError` |
|
|
446
|
+
|
|
447
|
+
### Naming Helpers
|
|
448
|
+
|
|
449
|
+
| Function | Description |
|
|
450
|
+
|---|---|
|
|
451
|
+
| `toCamelCaseSingular(name, table?)` | Server-aware singular name |
|
|
452
|
+
| `toCamelCasePlural(name, table?)` | Server-aware plural name |
|
|
453
|
+
| `toCreateMutationName(name, table?)` | Create mutation operation name |
|
|
454
|
+
| `toUpdateMutationName(name, table?)` | Update mutation operation name |
|
|
455
|
+
| `toDeleteMutationName(name, table?)` | Delete mutation operation name |
|
|
456
|
+
| `toPatchFieldName(name, table?)` | Patch field name in update input |
|
|
457
|
+
| `toFilterTypeName(name, table?)` | Filter type name |
|
|
458
|
+
| `toOrderByEnumValue(fieldName)` | Convert field name to OrderBy enum value |
|
|
459
|
+
| `normalizeInflectionValue(value, fallback)` | Safe inflection value lookup |
|
|
460
|
+
|
|
461
|
+
### Types
|
|
462
|
+
|
|
463
|
+
| Type | Description |
|
|
464
|
+
|---|---|
|
|
465
|
+
| `CleanTable` | Normalized table metadata with fields, relations, inflection, query names |
|
|
466
|
+
| `CleanField` | Field with type info, `isNotNull`, `hasDefault` |
|
|
467
|
+
| `QueryOptions` | Options for `buildSelect` (pagination, filters, field selection, relation mapping) |
|
|
468
|
+
| `MutationOptions` | Options for mutation builders |
|
|
469
|
+
| `FieldSelection` | Field selection presets and custom configuration |
|
|
470
|
+
| `ConnectionResult<T>` | Relay-style connection result type |
|
|
471
|
+
| `Filter` | PostGraphile connection filter type |
|
|
472
|
+
|
|
127
473
|
---
|
|
128
474
|
|
|
129
475
|
## Education and Tutorials
|
package/ast.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ASTFunctionParams,
|
|
3
|
-
export declare const getAll: ({ queryName, operationName,
|
|
1
|
+
import type { DocumentNode, FieldNode } from 'graphql';
|
|
2
|
+
import type { ASTFunctionParams, MutationASTParams, QueryFieldSelection } from './types';
|
|
3
|
+
export declare const getAll: ({ queryName, operationName, selection, }: ASTFunctionParams) => DocumentNode;
|
|
4
4
|
export declare const getCount: ({ queryName, operationName, query, }: Omit<ASTFunctionParams, "selection">) => DocumentNode;
|
|
5
5
|
export declare const getMany: ({ builder, queryName, operationName, query, selection, }: ASTFunctionParams) => DocumentNode;
|
|
6
6
|
export declare const getOne: ({ queryName, operationName, query, selection, }: ASTFunctionParams) => DocumentNode;
|
|
7
7
|
export declare const createOne: ({ mutationName, operationName, mutation, selection, }: MutationASTParams) => DocumentNode;
|
|
8
8
|
export declare const patchOne: ({ mutationName, operationName, mutation, selection, }: MutationASTParams) => DocumentNode;
|
|
9
9
|
export declare const deleteOne: ({ mutationName, operationName, mutation, }: Omit<MutationASTParams, "selection">) => DocumentNode;
|
|
10
|
-
export declare function getSelections(selection?:
|
|
10
|
+
export declare function getSelections(selection?: QueryFieldSelection[]): FieldNode[];
|
package/ast.js
CHANGED
|
@@ -37,7 +37,7 @@ exports.deleteOne = exports.patchOne = exports.createOne = exports.getOne = expo
|
|
|
37
37
|
exports.getSelections = getSelections;
|
|
38
38
|
const t = __importStar(require("gql-ast"));
|
|
39
39
|
const graphql_1 = require("graphql");
|
|
40
|
-
const
|
|
40
|
+
const inflekt_1 = require("inflekt");
|
|
41
41
|
const custom_ast_1 = require("./custom-ast");
|
|
42
42
|
const NON_MUTABLE_PROPS = ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'];
|
|
43
43
|
const objectToArray = (obj) => Object.keys(obj).map((k) => ({
|
|
@@ -85,7 +85,7 @@ const createGqlMutation = ({ operationName, mutationName, selectArgs, selections
|
|
|
85
85
|
],
|
|
86
86
|
});
|
|
87
87
|
};
|
|
88
|
-
const getAll = ({ queryName, operationName,
|
|
88
|
+
const getAll = ({ queryName, operationName, selection, }) => {
|
|
89
89
|
const selections = getSelections(selection);
|
|
90
90
|
const opSel = [
|
|
91
91
|
t.field({
|
|
@@ -209,7 +209,10 @@ const getMany = ({ builder, queryName, operationName, query, selection, }) => {
|
|
|
209
209
|
t.argument({ name: 'offset', value: t.variable({ name: 'offset' }) }),
|
|
210
210
|
t.argument({ name: 'after', value: t.variable({ name: 'after' }) }),
|
|
211
211
|
t.argument({ name: 'before', value: t.variable({ name: 'before' }) }),
|
|
212
|
-
t.argument({
|
|
212
|
+
t.argument({
|
|
213
|
+
name: 'condition',
|
|
214
|
+
value: t.variable({ name: 'condition' }),
|
|
215
|
+
}),
|
|
213
216
|
t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }),
|
|
214
217
|
t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }),
|
|
215
218
|
];
|
|
@@ -318,7 +321,7 @@ const createOne = ({ mutationName, operationName, mutation, selection, }) => {
|
|
|
318
321
|
if (!mutation.properties?.input?.properties) {
|
|
319
322
|
throw new Error(`No input field for mutation: ${mutationName}`);
|
|
320
323
|
}
|
|
321
|
-
const modelName = (0,
|
|
324
|
+
const modelName = (0, inflekt_1.camelize)([(0, inflekt_1.singularize)(mutation.model)].join('_'), true);
|
|
322
325
|
const inputProperties = mutation.properties.input
|
|
323
326
|
.properties;
|
|
324
327
|
const modelProperties = inputProperties[modelName];
|
|
@@ -364,7 +367,7 @@ const patchOne = ({ mutationName, operationName, mutation, selection, }) => {
|
|
|
364
367
|
if (!mutation.properties?.input?.properties) {
|
|
365
368
|
throw new Error(`No input field for mutation: ${mutationName}`);
|
|
366
369
|
}
|
|
367
|
-
const modelName = (0,
|
|
370
|
+
const modelName = (0, inflekt_1.camelize)([(0, inflekt_1.singularize)(mutation.model)].join('_'), true);
|
|
368
371
|
const inputProperties = mutation.properties.input
|
|
369
372
|
.properties;
|
|
370
373
|
const patchProperties = inputProperties['patch'];
|
|
@@ -417,7 +420,7 @@ const deleteOne = ({ mutationName, operationName, mutation, }) => {
|
|
|
417
420
|
if (!mutation.properties?.input?.properties) {
|
|
418
421
|
throw new Error(`No input field for mutation: ${mutationName}`);
|
|
419
422
|
}
|
|
420
|
-
const modelName = (0,
|
|
423
|
+
const modelName = (0, inflekt_1.camelize)([(0, inflekt_1.singularize)(mutation.model)].join('_'), true);
|
|
421
424
|
const inputProperties = mutation.properties.input
|
|
422
425
|
.properties;
|
|
423
426
|
const deleteAttrs = objectToArray(inputProperties);
|
|
@@ -463,9 +466,21 @@ const deleteOne = ({ mutationName, operationName, mutation, }) => {
|
|
|
463
466
|
exports.deleteOne = deleteOne;
|
|
464
467
|
function getSelections(selection = []) {
|
|
465
468
|
const selectionAst = (field) => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
+
if (typeof field === 'string') {
|
|
470
|
+
return t.field({ name: field });
|
|
471
|
+
}
|
|
472
|
+
// Check if fieldDefn has MetaField shape (has type.pgType)
|
|
473
|
+
const fieldDefn = field.fieldDefn;
|
|
474
|
+
if (fieldDefn &&
|
|
475
|
+
'type' in fieldDefn &&
|
|
476
|
+
fieldDefn.type &&
|
|
477
|
+
typeof fieldDefn.type === 'object' &&
|
|
478
|
+
'pgType' in fieldDefn.type) {
|
|
479
|
+
const customAst = (0, custom_ast_1.getCustomAst)(fieldDefn);
|
|
480
|
+
if (customAst)
|
|
481
|
+
return customAst;
|
|
482
|
+
}
|
|
483
|
+
return t.field({ name: field.name });
|
|
469
484
|
};
|
|
470
485
|
return selection
|
|
471
486
|
.map((selectionDefn) => {
|