@smartive/graphql-magic 15.2.1 → 15.3.1

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
- ## [15.2.1](https://github.com/smartive/graphql-magic/compare/v15.2.0...v15.2.1) (2024-04-03)
1
+ ## [15.3.1](https://github.com/smartive/graphql-magic/compare/v15.3.0...v15.3.1) (2024-04-04)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * Finish intro ([4ecc445](https://github.com/smartive/graphql-magic/commit/4ecc44575901cbe731dca7b70f109da6eaa847ba))
6
+ * **deps:** update docusaurus monorepo to v3.2.0 ([485e127](https://github.com/smartive/graphql-magic/commit/485e127761189bf0c104e6d0a90115d3d21e6c0c))
@@ -1,7 +1,3 @@
1
- ---
2
- sidebar_position: 1
3
- ---
4
-
5
1
  # Tutorial
6
2
 
7
3
  Let's create a blog with `graphql-magic`!
@@ -19,7 +15,7 @@ cd magic-blog
19
15
 
20
16
  Replace `src/app/globals.css`:
21
17
 
22
- ```
18
+ ```css
23
19
  @tailwind base;
24
20
  @tailwind components;
25
21
  @tailwind utilities;
@@ -71,7 +67,7 @@ label span {
71
67
 
72
68
  Replace `src/app/page.tsx`:
73
69
 
74
- ```
70
+ ```tsx
75
71
  export default async function Home() {
76
72
  return <main>
77
73
  <nav>
@@ -83,7 +79,7 @@ export default async function Home() {
83
79
 
84
80
  Start the website:
85
81
 
86
- ```
82
+ ```bash
87
83
  npm run dev
88
84
  ```
89
85
 
@@ -91,7 +87,7 @@ npm run dev
91
87
 
92
88
  Add this setting to `next.config.mjs`:
93
89
 
94
- ```
90
+ ```ts
95
91
  const nextConfig = {
96
92
  experimental: {
97
93
  serverComponentsExternalPackages: ['knex'],
@@ -101,13 +97,13 @@ const nextConfig = {
101
97
 
102
98
  Install `@smartive/graphql-magic`:
103
99
 
104
- ```
100
+ ```bash
105
101
  npm install @smartive/graphql-magic
106
102
  ```
107
103
 
108
104
  Run the gqm cli:
109
105
 
110
- ```
106
+ ```bash
111
107
  npx gqm generate
112
108
  ```
113
109
 
@@ -116,7 +112,7 @@ npx gqm generate
116
112
  Let's boot a local database instance.
117
113
  Create the following `docker-compose.yml`:
118
114
 
119
- ```
115
+ ```yml
120
116
  version: '3.4'
121
117
  services:
122
118
  postgres:
@@ -136,7 +132,7 @@ Then start it with `docker-compose up`.
136
132
 
137
133
  Generate the first migration:
138
134
 
139
- ```
135
+ ```bash
140
136
  npx gqm generate-migration
141
137
  ```
142
138
 
@@ -145,8 +141,8 @@ Enter a migration name, e.g. "setup".
145
141
 
146
142
  Run the migration
147
143
 
148
- ```
149
- npx env-cmd knex migrate:up
144
+ ```bash
145
+ npx env-cmd knex migrate:latest
150
146
  ```
151
147
 
152
148
  ### Auth setup
@@ -156,7 +152,7 @@ For example, follow [this tutorial](https://auth0.com/docs/quickstart/webapp/nex
156
152
 
157
153
  Assuming you used auth0, here's a bare-bones version of what `src/app/page.tsx` could look like:
158
154
 
159
- ```
155
+ ```tsx
160
156
  import { getSession } from '@auth0/nextjs-auth0';
161
157
 
162
158
  export default async function Page() {
@@ -179,7 +175,7 @@ Now, we need to ensure that the user is stored in the database.
179
175
 
180
176
  First extend the user model in `src/config/models.ts` with the following fields:
181
177
 
182
- ```
178
+ ```tsx
183
179
  fields: [
184
180
  {
185
181
  name: 'authId',
@@ -196,25 +192,25 @@ First extend the user model in `src/config/models.ts` with the following fields:
196
192
 
197
193
  The models have changed, generate the new types:
198
194
 
199
- ```
195
+ ```bash
200
196
  npx gqm generate
201
197
  ```
202
198
 
203
199
  Generate the new migration:
204
200
 
205
- ```
201
+ ```bash
206
202
  npx gqm generate-migration
207
203
  ```
208
204
 
209
205
  Edit the generated migration, then run it
210
206
 
211
- ```
212
- npx env-cmd knex migrate:up
207
+ ```bash
208
+ npx env-cmd knex migrate:latest
213
209
  ```
214
210
 
215
211
  Now let's implement the `// TODO: get user` part in the `src/graphql/execute.ts` file
216
212
 
217
- ```
213
+ ```ts
218
214
  const session = await getSession();
219
215
  if (session) {
220
216
  let dbUser = await db('User').where({ authId: session.user.sid }).first();
@@ -235,7 +231,7 @@ Now let's implement the `// TODO: get user` part in the `src/graphql/execute.ts`
235
231
 
236
232
  Extend `src/graphql/client/queries/get-me.ts` to also fetch the user's username:
237
233
 
238
- ```
234
+ ```ts
239
235
  import { gql } from '@smartive/graphql-magic';
240
236
 
241
237
  export const GET_ME = gql`
@@ -250,13 +246,13 @@ export const GET_ME = gql`
250
246
 
251
247
  Generate the new types:
252
248
 
253
- ```
249
+ ```bash
254
250
  npx gqm generate
255
251
  ```
256
252
 
257
253
  Now, let's modify `src/app/page.tsx` so that it fetches the user from the database:
258
254
 
259
- ```
255
+ ```tsx
260
256
  import { GetMeQuery } from "@/generated/client";
261
257
  import { GET_ME } from "@/graphql/client/queries/get-me";
262
258
  import { executeGraphql } from "@/graphql/execute";
@@ -279,7 +275,7 @@ export default async function Home() {
279
275
 
280
276
  Let's make a blog out of this app by adding new models in `src/config/models.ts`:
281
277
 
282
- ```
278
+ ```ts
283
279
  {
284
280
  kind: 'entity',
285
281
  name: 'Post',
@@ -330,14 +326,14 @@ Let's make a blog out of this app by adding new models in `src/config/models.ts`
330
326
 
331
327
  Generate and run the new migrations and generate the new models:
332
328
 
333
- ```
329
+ ```bash
334
330
  npx gqm generate-migration
335
- npx env-cmd knex migrate:up
331
+ npx env-cmd knex migrate:latest
336
332
  ```
337
333
 
338
334
  Create a new query `src/graphql/client/queries/get-posts.ts`:
339
335
 
340
- ```
336
+ ```ts
341
337
  import { gql } from '@smartive/graphql-magic';
342
338
 
343
339
  export const GET_POSTS = gql`
@@ -363,14 +359,14 @@ export const GET_POSTS = gql`
363
359
 
364
360
  Generate the new types:
365
361
 
366
- ```
362
+ ```bash
367
363
  npx gqm generate
368
364
  ```
369
365
 
370
366
  Now add all the logic to create and display posts and comments to `src/app/page.tsx`
371
367
 
372
368
 
373
- ```
369
+ ```tsx
374
370
  import { CreateCommentMutationMutation, CreateCommentMutationMutationVariables, CreatePostMutationMutation, CreatePostMutationMutationVariables, GetMeQuery, GetPostsQuery } from "@/generated/client";
375
371
  import { CREATE_COMMENT, CREATE_POST } from "@/generated/client/mutations";
376
372
  import { GET_ME } from "@/graphql/client/queries/get-me";
@@ -0,0 +1,3 @@
1
+ # Admin UI (TODO)
2
+
3
+ TODO
@@ -0,0 +1,283 @@
1
+ # Models
2
+
3
+ The source of truth for `graphql-magic` is the `models` object, usually defined in `src/config/models.ts`. This is the minimal models:
4
+
5
+ ```ts
6
+ const modelDefinitions: ModelDefinitions = [
7
+ {
8
+ kind: 'entity',
9
+ name: 'User',
10
+ fields: [
11
+ ]
12
+ },
13
+ ]
14
+
15
+ export const models = new Models(modelDefinitions)
16
+ ```
17
+
18
+ Models can have the following kinds:
19
+
20
+ ## Entities
21
+
22
+ The most powerful model kind. Entities are models that are stored in database tables, and are defined with `kind: 'entity'`:
23
+
24
+ ```ts
25
+ {
26
+ kind: 'entity',
27
+ name: 'Post',
28
+ fields: [
29
+ // ...
30
+ ]
31
+ }
32
+ ```
33
+
34
+ These are the entity options
35
+
36
+ ### description
37
+
38
+ Will appear as graphql description
39
+
40
+ ### plural
41
+
42
+ `graphql-magic` detects natural language plurals of model names with the `inflection` npm package. You can override this here.
43
+
44
+ ### creatable
45
+
46
+ When `creatable` is `true`, the entity can be created using a dedicated graphql `create<ModelName>` mutation.
47
+
48
+ For this to work, at least one entity field needs to be marked as `creatable`.
49
+
50
+ `creatable` also accepts an object to override properties of the implicitly generated `createdBy` and `createdAt` fields.
51
+
52
+ ### updatable
53
+
54
+ When `updatable` is `true`, the entity can be created using a dedicated graphql `delete<ModelName>` mutation.
55
+
56
+ For this to work, at least one entity field needs to be marked as `updatable`.
57
+
58
+ `updatable` also accepts an object to override properties of the implicitly generated `updatedBy` and `updatedAt` fields.
59
+
60
+ If a field is updatable, a `<ModelName>Revisions` table is created (containing only the updatable fields) and extended with each update.
61
+
62
+ ### deletable
63
+
64
+ When `deletable` is `true`, the entity can be created using a dedicated graphql `delete<ModelName>` mutation.
65
+
66
+ This is a soft delete (the `deleted` field is set to `true`), and the entity can be restored with the graphql `restore<ModelName>` mutation.
67
+
68
+ `deletable` also accepts an object to override properties of the implicitly generated `deleted`, `deletedBy` and `deletedAt` fields.
69
+
70
+ ### queriable
71
+
72
+ When `queriable` is `true` a graphql `Query` becomes available to fetch exactly one element by id.
73
+
74
+ For example, with
75
+
76
+ ```ts
77
+ {
78
+ kind: 'entity',
79
+ name: 'Post',
80
+ queriable: true
81
+ ...
82
+ }
83
+ ```
84
+
85
+ the following graphql query becomes possible
86
+
87
+ ```graphql
88
+ query {
89
+ post(where: { id: "bf9496bb-9302-4528-aebc-c97ae49c52fa"}) {
90
+ title
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### listQueriable
96
+
97
+ When `listQueriable` is `true` a graphql `Query` becomes available to fetch a list of elements of this model.
98
+
99
+ For example, with
100
+
101
+ ```ts
102
+ {
103
+ kind: 'entity',
104
+ name: 'Post',
105
+ listQueriable: true
106
+ ...
107
+ }
108
+ ```
109
+
110
+ the following graphql query becomes possible
111
+
112
+ ```graphql
113
+ query {
114
+ posts {
115
+ title
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### displayField
121
+
122
+ The name of the field that ought to be used as display value, e.g. a `Post`'s `title`.
123
+
124
+ ### defaultOrderBy
125
+
126
+ An array of orders with the same structure as the `orderBy` parameters in graphql queries. The implicit default order by is `[{ createdAt: 'DESC }]`.
127
+
128
+ ### fields
129
+
130
+ An array of fields. See [fields](./fields.md)
131
+
132
+
133
+ ## Scalar
134
+
135
+ Used for graphql scalars, e.g.
136
+
137
+ ```ts
138
+ {
139
+ kind: 'Scalar',
140
+ name: 'DateTime'
141
+ }
142
+ ```
143
+
144
+ ## Enum
145
+
146
+ An enum that is available as type in the database:
147
+
148
+ ```ts
149
+ {
150
+ kind: 'enum',
151
+ name: 'Role',
152
+ values: ['ADMIN', 'MODERATOR', 'USER']
153
+ }
154
+ ```
155
+
156
+ ## Raw enum
157
+
158
+ An enum that is *not* available as type in the database:
159
+
160
+ ```ts
161
+ {
162
+ kind: 'raw-enum',
163
+ name: 'Role',
164
+ values: ['ADMIN', 'MODERATOR', 'USER']
165
+ }
166
+ ```
167
+
168
+ ## Interface
169
+
170
+ Types that can be inherited from, e.g.
171
+
172
+ ```ts
173
+ {
174
+ kind: 'interface',
175
+ name: 'WithContent',
176
+ fields: [
177
+ {
178
+ type: 'String',
179
+ name: 'content'
180
+ }
181
+ ]
182
+ },
183
+ {
184
+ kind: 'entity',
185
+ name: 'Post',
186
+ interfaces: ['WithContent']
187
+ fields: [
188
+ {
189
+ type: 'String',
190
+ name: 'content'
191
+ }
192
+ ]
193
+ }
194
+ ```
195
+
196
+ ## Object
197
+
198
+ Custom types that *don't* correspond to database tables. To be used e.g. as return types for custom resolvers or JSON fields. These can also be used to extend `Query` or `Mutation` which are themselves of that type. E.g.
199
+
200
+ ```ts
201
+ {
202
+ kind: 'object',
203
+ name: 'Stats',
204
+ fields: [
205
+ {
206
+ name: 'usersCount'
207
+ type: 'Int'
208
+ },
209
+ {
210
+ name: 'postsCount',
211
+ type: 'Int'
212
+ }
213
+ ]
214
+ },
215
+ {
216
+ kind: 'object',
217
+ name: 'Query',
218
+ fields: [
219
+ // You'll need to define a custom resolver for this one
220
+ {
221
+ kind: 'custom',
222
+ name: 'stats',
223
+ type: 'Stats'
224
+ }
225
+ ]
226
+ }
227
+ ```
228
+
229
+ will make this query possible:
230
+
231
+ ```graphql
232
+ query {
233
+ stats {
234
+ usersCount
235
+ postsCount
236
+ }
237
+ }
238
+ ```
239
+
240
+ ## Input
241
+
242
+ A custom input type. To be combined with custom mutations, e.g.
243
+
244
+ ```ts
245
+ {
246
+ kind: 'input',
247
+ name: 'BulkDeleteWhereInput'
248
+ fields: [
249
+ {
250
+ kind: 'ID',
251
+ name: 'ids',
252
+ list: true
253
+ }
254
+ ]
255
+ },
256
+ {
257
+ kind: 'object',
258
+ name: 'Mutation',
259
+ fields: [
260
+ // You'll need to define a custom resolver for this one
261
+ {
262
+ kind: 'custom',
263
+ name: 'bulkDelete',
264
+ args: [
265
+ {
266
+ kind: 'custom',
267
+ name: 'where',
268
+ type: 'BulkDeleteWhereInput'
269
+ }
270
+ ]
271
+ type: 'Boolean'
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ will make this mutation possible:
278
+
279
+ ```graphql
280
+ mutation {
281
+ bulkDelete(where: { ids: [...]})
282
+ }
283
+ ```