@pothos/plugin-prisma 0.18.0 → 3.1.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.
Files changed (165) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +420 -315
  3. package/esm/field-builder.d.ts.map +1 -1
  4. package/esm/field-builder.js +19 -6
  5. package/esm/field-builder.js.map +1 -1
  6. package/esm/generator.js +4 -0
  7. package/esm/generator.js.map +1 -1
  8. package/esm/global-types.d.ts +23 -5
  9. package/esm/global-types.d.ts.map +1 -1
  10. package/esm/index.d.ts +4 -1
  11. package/esm/index.d.ts.map +1 -1
  12. package/esm/index.js +46 -1
  13. package/esm/index.js.map +1 -1
  14. package/esm/model-loader.d.ts +5 -6
  15. package/esm/model-loader.d.ts.map +1 -1
  16. package/esm/model-loader.js +18 -78
  17. package/esm/model-loader.js.map +1 -1
  18. package/esm/prisma-field-builder.d.ts +25 -6
  19. package/esm/prisma-field-builder.d.ts.map +1 -1
  20. package/esm/prisma-field-builder.js +86 -118
  21. package/esm/prisma-field-builder.js.map +1 -1
  22. package/esm/schema-builder.js +14 -4
  23. package/esm/schema-builder.js.map +1 -1
  24. package/esm/types.d.ts +74 -77
  25. package/esm/types.d.ts.map +1 -1
  26. package/esm/types.js +1 -0
  27. package/esm/types.js.map +1 -1
  28. package/esm/{cursors.d.ts → util/cursors.d.ts} +5 -5
  29. package/esm/util/cursors.d.ts.map +1 -0
  30. package/esm/{cursors.js → util/cursors.js} +0 -0
  31. package/esm/util/cursors.js.map +1 -0
  32. package/{lib/refs.d.ts → esm/util/datamodel.d.ts} +4 -8
  33. package/esm/util/datamodel.d.ts.map +1 -0
  34. package/esm/{refs.js → util/datamodel.js} +2 -25
  35. package/esm/util/datamodel.js.map +1 -0
  36. package/esm/util/deep-equal.d.ts +2 -0
  37. package/esm/util/deep-equal.d.ts.map +1 -0
  38. package/esm/util/deep-equal.js +39 -0
  39. package/esm/util/deep-equal.js.map +1 -0
  40. package/esm/util/loader-map.d.ts +6 -0
  41. package/esm/util/loader-map.d.ts.map +1 -0
  42. package/esm/{loader-map.js → util/loader-map.js} +10 -12
  43. package/esm/util/loader-map.js.map +1 -0
  44. package/esm/util/map-query.d.ts +6 -0
  45. package/esm/util/map-query.d.ts.map +1 -0
  46. package/esm/util/map-query.js +169 -0
  47. package/esm/util/map-query.js.map +1 -0
  48. package/esm/util/relation-map.d.ts +9 -0
  49. package/esm/util/relation-map.d.ts.map +1 -0
  50. package/esm/util/relation-map.js +20 -0
  51. package/esm/util/relation-map.js.map +1 -0
  52. package/esm/util/selections.d.ts +20 -0
  53. package/esm/util/selections.d.ts.map +1 -0
  54. package/esm/util/selections.js +139 -0
  55. package/esm/util/selections.js.map +1 -0
  56. package/lib/field-builder.d.ts.map +1 -1
  57. package/lib/field-builder.js +30 -13
  58. package/lib/field-builder.js.map +1 -1
  59. package/lib/generator.js +9 -1
  60. package/lib/generator.js.map +1 -1
  61. package/lib/global-types.d.ts +23 -5
  62. package/lib/global-types.d.ts.map +1 -1
  63. package/lib/index.d.ts +4 -1
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js +50 -1
  66. package/lib/index.js.map +1 -1
  67. package/lib/model-loader.d.ts +5 -6
  68. package/lib/model-loader.d.ts.map +1 -1
  69. package/lib/model-loader.js +19 -79
  70. package/lib/model-loader.js.map +1 -1
  71. package/lib/prisma-field-builder.d.ts +25 -6
  72. package/lib/prisma-field-builder.d.ts.map +1 -1
  73. package/lib/prisma-field-builder.js +92 -124
  74. package/lib/prisma-field-builder.js.map +1 -1
  75. package/lib/schema-builder.js +22 -8
  76. package/lib/schema-builder.js.map +1 -1
  77. package/lib/types.d.ts +74 -77
  78. package/lib/types.d.ts.map +1 -1
  79. package/lib/types.js +2 -0
  80. package/lib/types.js.map +1 -1
  81. package/lib/{cursors.d.ts → util/cursors.d.ts} +5 -5
  82. package/lib/util/cursors.d.ts.map +1 -0
  83. package/lib/{cursors.js → util/cursors.js} +0 -0
  84. package/lib/util/cursors.js.map +1 -0
  85. package/{esm/refs.d.ts → lib/util/datamodel.d.ts} +4 -8
  86. package/lib/util/datamodel.d.ts.map +1 -0
  87. package/lib/{refs.js → util/datamodel.js} +3 -29
  88. package/lib/util/datamodel.js.map +1 -0
  89. package/lib/util/deep-equal.d.ts +2 -0
  90. package/lib/util/deep-equal.d.ts.map +1 -0
  91. package/lib/util/deep-equal.js +43 -0
  92. package/lib/util/deep-equal.js.map +1 -0
  93. package/lib/util/loader-map.d.ts +6 -0
  94. package/lib/util/loader-map.d.ts.map +1 -0
  95. package/lib/{loader-map.js → util/loader-map.js} +10 -12
  96. package/lib/util/loader-map.js.map +1 -0
  97. package/lib/util/map-query.d.ts +6 -0
  98. package/lib/util/map-query.d.ts.map +1 -0
  99. package/lib/util/map-query.js +175 -0
  100. package/lib/util/map-query.js.map +1 -0
  101. package/lib/util/relation-map.d.ts +9 -0
  102. package/lib/util/relation-map.d.ts.map +1 -0
  103. package/lib/util/relation-map.js +24 -0
  104. package/lib/util/relation-map.js.map +1 -0
  105. package/lib/util/selections.d.ts +20 -0
  106. package/lib/util/selections.d.ts.map +1 -0
  107. package/lib/util/selections.js +148 -0
  108. package/lib/util/selections.js.map +1 -0
  109. package/package.json +8 -8
  110. package/src/field-builder.ts +22 -5
  111. package/src/generator.ts +18 -0
  112. package/src/global-types.ts +59 -12
  113. package/src/index.ts +75 -1
  114. package/src/model-loader.ts +27 -106
  115. package/src/prisma-field-builder.ts +195 -152
  116. package/src/schema-builder.ts +28 -7
  117. package/src/types.ts +155 -102
  118. package/src/{cursors.ts → util/cursors.ts} +3 -3
  119. package/src/{refs.ts → util/datamodel.ts} +3 -44
  120. package/src/util/deep-equal.ts +51 -0
  121. package/src/{loader-map.ts → util/loader-map.ts} +13 -13
  122. package/src/util/map-query.ts +327 -0
  123. package/src/util/relation-map.ts +36 -0
  124. package/src/util/selections.ts +192 -0
  125. package/esm/cursors.d.ts.map +0 -1
  126. package/esm/cursors.js.map +0 -1
  127. package/esm/loader-map.d.ts +0 -6
  128. package/esm/loader-map.d.ts.map +0 -1
  129. package/esm/loader-map.js.map +0 -1
  130. package/esm/refs.d.ts.map +0 -1
  131. package/esm/refs.js.map +0 -1
  132. package/esm/util/index.d.ts +0 -5
  133. package/esm/util/index.d.ts.map +0 -1
  134. package/esm/util/index.js +0 -16
  135. package/esm/util/index.js.map +0 -1
  136. package/esm/util/map-includes.d.ts +0 -6
  137. package/esm/util/map-includes.d.ts.map +0 -1
  138. package/esm/util/map-includes.js +0 -184
  139. package/esm/util/map-includes.js.map +0 -1
  140. package/esm/util/merge-includes.d.ts +0 -3
  141. package/esm/util/merge-includes.d.ts.map +0 -1
  142. package/esm/util/merge-includes.js +0 -91
  143. package/esm/util/merge-includes.js.map +0 -1
  144. package/lib/cursors.d.ts.map +0 -1
  145. package/lib/cursors.js.map +0 -1
  146. package/lib/loader-map.d.ts +0 -6
  147. package/lib/loader-map.d.ts.map +0 -1
  148. package/lib/loader-map.js.map +0 -1
  149. package/lib/refs.d.ts.map +0 -1
  150. package/lib/refs.js.map +0 -1
  151. package/lib/util/index.d.ts +0 -5
  152. package/lib/util/index.d.ts.map +0 -1
  153. package/lib/util/index.js +0 -30
  154. package/lib/util/index.js.map +0 -1
  155. package/lib/util/map-includes.d.ts +0 -6
  156. package/lib/util/map-includes.d.ts.map +0 -1
  157. package/lib/util/map-includes.js +0 -189
  158. package/lib/util/map-includes.js.map +0 -1
  159. package/lib/util/merge-includes.d.ts +0 -3
  160. package/lib/util/merge-includes.d.ts.map +0 -1
  161. package/lib/util/merge-includes.js +0 -96
  162. package/lib/util/merge-includes.js.map +0 -1
  163. package/src/util/index.ts +0 -26
  164. package/src/util/map-includes.ts +0 -328
  165. package/src/util/merge-includes.ts +0 -121
package/README.md CHANGED
@@ -4,70 +4,79 @@ This plugin provides tighter integration with prisma, making it easier to define
4
4
  types, and helps solve n+1 queries for relations. It also has integrations for the relay plugin to
5
5
  make defining nodes and connections easy and efficient.
6
6
 
7
- ## Disclaimers
8
-
9
- This plugin is experimental, and will have some breaking changes in the future. DO NOT USE this
10
- plugin unless you are willing to deal with breaking changes in upcoming versions. This plugin may
11
- introduce BREAKING changes in minor versions until it's major version has been increased above 0.
12
-
13
- This plugin is NOT required to build graphs backed by prisma models, and I would not recommend using
14
- it unless you have a solid understanding of how it will construct queries.
15
-
16
- This plugin will allow common queries to be resolved through a single prisma query (prisma may still
17
- turn this into multiple SQL queries), and provides reasonable, predictable and safe fallbacks for
18
- more complex queries and edge cases. That being said, graphql APIs are complex, and it is important
19
- to understand the queries your API is capable of executing.
20
-
21
- The way this plugin resolves queries is designed to be efficient, while still being predictable and
22
- easy to understand. Tools that try to automatically generate queries are often hard to understand
23
- and reason about, so this plugin tries to make things as clear as possible by providing query
24
- options to resolvers and a loading user code to initiate the actual queries. The options generally
25
- only contain `include`s for nested relations (connection fields provide more complex query options).
26
- The exception to this, is that we provide a default resolver for relations that can handle querying
27
- for a relation if data was not pre-loaded by a parent field. This query used by this resolver is
28
- simple, and described in detail below.
29
-
30
- With this simple approach, we get an API that is easy to understand, but still provides a lot of
31
- value and functionality.
7
+ This plugin is NOT required to use prisma with Pothos, but does make things a lot easier and more
8
+ efficient. See the [Using Prisma without a plugin](#using-prisma-without-a-plugin) section below for
9
+ more details.
10
+
11
+ ## Features
12
+
13
+ - 🎨 Quickly define GraphQL types based on your Prisma models
14
+ - 🦺 Strong type-safety throughout the entire API
15
+ - 🤝 Automatically resolve relationships defined in your database
16
+ - 🎣 Automatic Query optimization to efficiently load the specific data needed to resolve a query
17
+ (solves common N+1 issues)
18
+ - 💅 Types and fields in GraphQL schema are not implicitly tied to the column names or types in your
19
+ database.
20
+ - 🔀 Relay integration for defining nodes and connections that can be efficiently loaded.
21
+ - 📚 Supports multiple GraphQL models based on the same Database model
22
+ - 🧮 Count fields can easily be added to objects and connections
32
23
 
33
24
  ## Example
34
25
 
35
26
  Here is a quick example of what an API using this plugin might look like. There is a more thorough
36
27
  breakdown of what the methods and options used in the example below.
37
28
 
38
- If you are looking for an example integrated with the
39
- [relay plugin](https://pothos-graphql.dev/docs/plugins/relay), see the
40
- [Relay integration](#relay-integration) section below.
41
-
42
29
  ```typescript
30
+ // Create an object type based on a prisma model
31
+ // without providing any custom type information
43
32
  builder.prismaObject('User', {
44
- include: {
45
- profile: true,
46
- },
33
+ // findUnique is explained more below, and is
34
+ // required to safely resolve queries in some edge cases
47
35
  findUnique: (user) => ({ id: user.id }),
48
36
  fields: (t) => ({
37
+ // expose fields from the database
49
38
  id: t.exposeID('id'),
50
39
  email: t.exposeString('email'),
51
40
  bio: t.string({
41
+ // automatically load the bio from the profile
42
+ // when this field is queried
43
+ select: {
44
+ profile: {
45
+ select: {
46
+ bio: true,
47
+ },
48
+ },
49
+ },
50
+ // user will be typed correctly to include the
51
+ // selected fields from above
52
52
  resolve: (user) => user.profile.bio,
53
53
  }),
54
+ // Load posts as list field.
54
55
  posts: t.relation('posts', {
55
56
  args: {
56
57
  oldestFirst: t.arg.boolean(),
57
58
  },
59
+ // Define custom query options that are applied when
60
+ // loading the post relation
58
61
  query: (args, context) => ({
59
62
  orderBy: {
60
63
  createdAt: args.oldestFirst ? 'asc' : 'desc',
61
64
  },
62
65
  }),
63
66
  }),
67
+ // creates relay connection that handles pagination
68
+ // using prisma's built in cursor based pagination
69
+ postsConnection: t.relatedConnection('posts', {
70
+ cursor: 'id',
71
+ }),
64
72
  }),
65
73
  });
66
74
 
67
- builder.prismaObject('Post', {
68
- findUnique: (post) => ({ id: post.id }),
75
+ // Create a relay node based a prisma model
76
+ builder.prismaNode('Post', {
77
+ findUnique: (id) => ({ id }),
78
+ id: { resolve: (post) => post.id },
69
79
  fields: (t) => ({
70
- id: t.exposeID('id'),
71
80
  title: t.exposeString('title'),
72
81
  author: t.relation('author'),
73
82
  }),
@@ -75,10 +84,13 @@ builder.prismaObject('Post', {
75
84
 
76
85
  builder.queryType({
77
86
  fields: (t) => ({
87
+ // Define a field that issues an optimized prisma query
78
88
  me: t.prismaField({
79
89
  type: 'User',
80
90
  resolve: async (query, root, args, ctx, info) =>
81
91
  prisma.user.findUnique({
92
+ // the `query` argument will add in `include`s or `select`s to
93
+ // resolve as much of the request in a single query as possible
82
94
  ...query,
83
95
  rejectOnNotFound: true,
84
96
  where: { id: ctx.userId },
@@ -129,153 +141,17 @@ query {
129
141
 
130
142
  Will result in 2 calls to prisma, one to resolve everything except `oldPosts`, and a second to
131
143
  resolve everything inside `oldPosts`. Prisma can only resolve each relation once in a single query,
132
- so we need a separate to handle the second `posts` relation. This may seem slightly magical, but
133
- should be predictable and hopefully easy to understand after reading the documentation below.
134
-
135
- ## Pothos + Prisma without a plugin
136
-
137
- If you just want learn about the plugin, feel free to skip this section, but understanding how to
138
- use prisma without a plugin may be useful for evaluating if this plugin is a good fit for your use
139
- case.
140
-
141
- Using prisma without a plugin is relatively straight forward using the `builder.objectRef` method.
142
-
143
- The easiest way to create types backed by prisma looks something like:
144
-
145
- ```typescript
146
- import { Post, PrismaClient, User } from '@prisma/client';
147
-
148
- const db = new PrismaClient();
149
- const UserObject = builder.objectRef<User>('User');
150
- const PostObject = builder.objectRef<Post>('Post');
151
-
152
- UserObject.implement({
153
- fields: (t) => ({
154
- id: t.exposeID('id'),
155
- email: t.exposeString('email'),
156
- posts: t.field({
157
- type: [PostObject],
158
- resolve: (user) =>
159
- db.post.findMany({
160
- where: { authorId: user.id },
161
- }),
162
- }),
163
- }),
164
- });
165
-
166
- PostObject.implement({
167
- fields: (t) => ({
168
- id: t.exposeID('id'),
169
- title: t.exposeString('title'),
170
- author: t.field({
171
- type: UserObject,
172
- resolve: (post) =>
173
- db.user.findUnique({ rejectOnNotFound: true, where: { id: post.authorId } }),
174
- }),
175
- }),
176
- });
177
-
178
- builder.queryType({
179
- fields: (t) => ({
180
- me: t.field({
181
- type: UserObject,
182
- resolve: (root, args, ctx) =>
183
- db.user.findUnique({ rejectOnNotFound: true, where: { id: ctx.userId } }),
184
- }),
185
- }),
186
- });
187
- ```
188
-
189
- This sets up User, and Post objects with a few fields, and a `me` query that returns the current
190
- user. There are a few things to note in this setup:
191
-
192
- 1. We split up the `builder.objectRef` and the `implement` calls, rather than calling
193
- `builder.objectRef(...).implement(...)`. This prevents typescript from getting tripped up by the
194
- circular references between posts and users.
195
- 2. We use rejectOnNotFound with our `findUnique` calls because those fields are not nullable.
196
- Without this option, prisma will return a null if the object is not found. An alternative is to
197
- mark these fields as nullable.
198
- 3. The refs to our object types are called `UserObject` and `PostObject`, this is because `User` and
199
- `Post` are the names of the types imported from prisma. We could instead alias the types when we
200
- import them so we can name the refs to our GraphQL types after the models.
201
-
202
- This setup is fairly simple, but it is easy to see the n+1 issues we might run into. Prisma helps
203
- with this by batching queries together, but there are also things we can do in our implementation to
204
- improve things.
205
-
206
- One thing we could do if we know we will usually be loading the author any time we load a post is to
207
- make the author part of shape required for a post:
208
-
209
- ```typescript
210
- const UserObject = builder.objectRef<User>('User');
211
- // We add the author here in the objectRef
212
- const PostObject = builder.objectRef<Post & { author: User }>('Post');
213
-
214
- UserObject.implement({
215
- fields: (t) => ({
216
- id: t.exposeID('id'),
217
- email: t.exposeString('email'),
218
- posts: t.field({
219
- type: [PostObject],
220
- resolve: (user) =>
221
- db.post.findMany({
222
- // We now need to include the author when we query for posts
223
- include: {
224
- author: true,
225
- },
226
- where: { authorId: user.id },
227
- }),
228
- }),
229
- }),
230
- });
231
-
232
- PostObject.implement({
233
- fields: (t) => ({
234
- id: t.exposeID('id'),
235
- title: t.exposeString('title'),
236
- author: t.field({
237
- type: UserObject,
238
- // Now we can just return the author from the post instead of querying for it
239
- resolve: (post) => post.author,
240
- }),
241
- }),
242
- });
243
- ```
244
-
245
- We may not always want to query for the author though, so we could make the author optional and fall
246
- back to using a query if it was not provided by the parent resolver:
247
-
248
- ```typescript
249
- const PostObject = builder.objectRef<Post & { author?: User }>('Post');
250
-
251
- PostObject.implement({
252
- fields: (t) => ({
253
- id: t.exposeID('id'),
254
- title: t.exposeString('title'),
255
- author: t.field({
256
- type: UserObject,
257
- resolve: (post) =>
258
- post.author ?? db.user.findUnique({ rejectOnNotFound: true, where: { id: post.authorId } }),
259
- }),
260
- }),
261
- });
262
- ```
144
+ so we need a separate to handle the second `posts` relation. These additional queries will use the
145
+ `findUnique` defined for the parent type to create a new efficient query to load any conflicting
146
+ relations.
263
147
 
264
- With this setup, a parent resolver has the option to include the author, but we have a fallback
265
- incase it does not.
266
-
267
- There are other patterns like dataloaders than can be used to reduce n+1 issues, and make your graph
268
- more efficient, but they are too complex to describe here.
269
-
270
- ## Usage
271
-
272
- ### Install
148
+ ## Install
273
149
 
274
150
  ```bash
275
151
  yarn add @pothos/plugin-prisma
276
152
  ```
277
153
 
278
- ### Setup
154
+ ## Setup
279
155
 
280
156
  This plugin requires a little more setup than other plugins because it integrates with the prisma to
281
157
  generate some types that help the plugin better understand your prisma schema. Previous versions of
@@ -283,7 +159,7 @@ this plugin used to infer all required types from the prisma client itself, but
283
159
  poor dev experience because the complex types slowed down editors, and some more advanced use cases
284
160
  could not be typed correctly.
285
161
 
286
- #### Add a the `pothos` generator to your prisma schema
162
+ ### Add a the `pothos` generator to your prisma schema
287
163
 
288
164
  ```
289
165
  generator pothos {
@@ -294,7 +170,7 @@ generator pothos {
294
170
  Now the types Pothos uses will be generated whenever you re-generate your prisma client. Run the
295
171
  following command to re-generate the client and create the new types:
296
172
 
297
- ```bash
173
+ ```sh
298
174
  npx prisma generate
299
175
  ```
300
176
 
@@ -315,14 +191,16 @@ generator pothos {
315
191
  }
316
192
  ```
317
193
 
318
- #### Set up the builder
194
+ ### Set up the builder
319
195
 
320
196
  ```typescript
321
197
  import SchemaBuilder from '@pothos/core';
322
198
  import { PrismaClient } from '@prisma/client';
323
199
  import PrismaPlugin from '@pothos/plugin-prisma';
324
- // This is the default location for the generator, but this can be customized as described above
325
- // Using a type only import will help avoid issues with undeclared exports in esm mode
200
+ // This is the default location for the generator, but this can be
201
+ // customized as described above.
202
+ // Using a type only import will help avoid issues with undeclared
203
+ // exports in esm mode
326
204
  import type PrismaTypes from '@pothos/plugin-prisma/generated';
327
205
 
328
206
  const prisma = new PrismaClient({});
@@ -339,9 +217,9 @@ const builder = new SchemaBuilder<{
339
217
 
340
218
  It is strongly recommended NOT to put your prisma client into `Context`. This will result in slower
341
219
  type-checking and a laggy developer experience in VSCode. See
342
- https://github.com/microsoft/TypeScript/issues/45405 for more details.
220
+ [this issue](https://github.com/microsoft/TypeScript/issues/45405) for more details.
343
221
 
344
- ### Creating some types with `builder.prismaObject`
222
+ ## Creating types with `builder.prismaObject`
345
223
 
346
224
  `builder.prismaObject` takes 2 arguments:
347
225
 
@@ -353,7 +231,7 @@ https://github.com/microsoft/TypeScript/issues/45405 for more details.
353
231
  builder.prismaObject('User', {
354
232
  // Optional name for the object, defaults to the name of the prisma model
355
233
  name: 'PostAuthor',
356
- findUnique: null,
234
+ findUnique: (user) => ({ id: user.id }),
357
235
  fields: (t) => ({
358
236
  id: t.exposeID('id'),
359
237
  email: t.exposeString('email'),
@@ -361,7 +239,7 @@ builder.prismaObject('User', {
361
239
  });
362
240
 
363
241
  builder.prismaObject('Post', {
364
- findUnique: null,
242
+ findUnique: (post) => ({ id: post.id }),
365
243
  fields: (t) => ({
366
244
  id: t.exposeID('id'),
367
245
  title: t.exposeString('title'),
@@ -370,12 +248,12 @@ builder.prismaObject('Post', {
370
248
  ```
371
249
 
372
250
  So far, this is just creating some simple object types. They work just like any other object type in
373
- Pothos. They main advantage of this is that we get the type information without using object refs,
374
- or needing imports from prisma client.
251
+ Pothos. The main advantage of this is that we get the type information without using object refs, or
252
+ needing imports from prisma client.
375
253
 
376
254
  The `findUnique` option is described more below.
377
255
 
378
- ### Adding prisma fields to non-prisma objects (including Query and Mutation)
256
+ ## Adding prisma fields to non-prisma objects (including Query and Mutation)
379
257
 
380
258
  There is a new `t.prismaField` method which can be used to define fields that resolve to your prisma
381
259
  types:
@@ -407,72 +285,29 @@ You do not need to use this method, and the `builder.prismaObject` method return
407
285
  can be used like any other object ref (with `t.field`), but using `t.prismaField` will allow you to
408
286
  take advantage of more efficient queries.
409
287
 
410
- The `query` object will contain an `include` object to pre-load data needed to resolve nested parts
411
- of the current query. This is based on fields defined with `t.relation` described below.
288
+ The `query` object will contain an object with `include` or `select` options to pre-load data needed
289
+ to resolve nested parts of the current query. The included/selected fields are based on which fields
290
+ are being queried, and the options provided when defining those fields and types.
412
291
 
413
- If there are no fields using `t.relation` in your query, everything is resolved exactly as it would
414
- be without this plugin.
415
-
416
- ### Adding relations
292
+ ## Adding relations
417
293
 
418
294
  You can add fields for relations using the `t.relation` method:
419
295
 
420
296
  ```typescript
421
- builder.prismaObject('User', {
422
- findUnique: null,
297
+ builder.queryType({
423
298
  fields: (t) => ({
424
- id: t.exposeID('id'),
425
- email: t.exposeString('email'),
426
- posts: t.relation('posts', {
427
- resolve: (query, user) =>
428
- db.post.findMany({
299
+ me: t.prismaField({
300
+ type: 'User',
301
+ resolve: async (query, root, args, ctx, info) =>
302
+ prisma.user.findUnique({
429
303
  ...query,
430
- where: { authorId: user.id },
304
+ rejectOnNotFound: true,
305
+ where: { id: ctx.userId },
431
306
  }),
432
307
  }),
433
308
  }),
434
309
  });
435
310
 
436
- builder.prismaObject('User', {
437
- findUnique: null,
438
- fields: (t) => ({
439
- id: t.exposeID('id'),
440
- title: t.exposeString('title'),
441
- author: t.relation('author', {
442
- resolve: (query, post) =>
443
- db.user.findUnique({ ...query, rejectOnNotFound: true, where: { id: post.authorId } }),
444
- }),
445
- }),
446
- });
447
- ```
448
-
449
- `t.relation` defines a field that can be pre-loaded by a parent resolver, and most of the time, the
450
- `resolve` function will NOT be called. This is VERY IMPORTANT to understand, because it is the
451
- biggest place where you can introduce inconsistencies into your API with this plugin. The `resolve`
452
- function is used to load the relationship if parent resolver did not pre-load the data needed to
453
- resolve the field. This happens for a number of reasons:
454
-
455
- 1. The parent object was not loaded through a field defined with `t.prismaField`, or `t.relation`
456
- 2. The `query` object for the parent field was not spread into the query
457
- 3. The graphql query requested multiple fields that depended on the same relationship (described
458
- more below)
459
-
460
- These are all okay, and expected situations. Graphql APIs are very flexible, and magically pushing
461
- everything into a single query is impossible for arbitrary queries. This is why we have a `resolve`
462
- function than can load the relation IF it was not already loaded by the parent.
463
-
464
- Like `t.prismaField`, the `resolve` function now as a new first argument that is a query that should
465
- be spread into the query, and is used to load nested relationships.
466
-
467
- ### Find Unique
468
-
469
- Because the `resolve` function is only used as a fallback, it is harder to test, and if done
470
- incorrectly can introduce inconsistencies. While it shouldn't be too hard to get right, it might be
471
- better to avoid it entirely. To do this, we can let the Prisma plugin generate these resolve
472
- functions for you in a consistent and predictable way. We can do this by providing a findUnique
473
- option for our object type. Defining a `findUnique` that is not null, will make `resolve` optional.
474
-
475
- ```typescript
476
311
  builder.prismaObject('User', {
477
312
  findUnique: (user) => ({ id: user.id }),
478
313
  fields: (t) => ({
@@ -492,23 +327,90 @@ builder.prismaObject('Post', {
492
327
  });
493
328
  ```
494
329
 
495
- This greatly simplifies our object types. In these cases, the fallback resolve functions will
496
- re-load the current object using the `findUnique` as the where clause, and then `include` the
497
- relation for the current field. This can produce a slightly less efficient query than a manual
498
- implementation because the parent object is re-loaded first, but it will batch multiple
499
- relationships into one query, and the findUnique queries should be very fast.
330
+ `t.relation` defines a field that can be pre-loaded by a parent resolver. This will create something
331
+ like `{ include: { author: true }}` that will be passed as part of the `query` argument of a
332
+ `prismaField` resolver. If the parent is another `relation` field, the includes will become nested,
333
+ and the full relation chain will be passed to the `prismaField` that started the chain.
500
334
 
501
- For example, if a `User` was loaded without pre-loading, and both a `posts` and a `profile` relation
502
- where requested, the generated prisma call would be something like:
335
+ For example the query:
503
336
 
504
- ```typescript
505
- prisma.user.findUnique({
506
- rejectOnNotFound: true,
507
- where: { id: user.id },
337
+ ```graphql
338
+ query {
339
+ me {
340
+ posts {
341
+ author {
342
+ id
343
+ }
344
+ }
345
+ }
346
+ }
347
+ ```
348
+
349
+ the `me` `prismaField` would receive something like the following as its query parameter:
350
+
351
+ ```ts
352
+ {
508
353
  include: {
509
- posts: true,
510
- profile: true,
511
- },
354
+ posts: {
355
+ include: {
356
+ author: true;
357
+ }
358
+ }
359
+ }
360
+ }
361
+ ```
362
+
363
+ This will work perfectly for the majority of queries. There are a number of edge cases that make it
364
+ impossible to resolve everything in a single query. When this happens the `findUnique` option is
365
+ used to ensure that everything is still loaded correctly, and split into as few efficient queries as
366
+ possible.
367
+
368
+ ### Find Unique
369
+
370
+ The `findUnique` function will receive an instance of the prisma model the current type is defining,
371
+ and should return an object that will be passed as a `where` in a `prisma.findUnique`. Generally,
372
+ this will just be something like: `user => { id: user.id }` where `id` is the primary key for the
373
+ table.
374
+
375
+ When the prisma plugin encounters a query where the requirements for a field can not be satisfied,
376
+ it will call findUnique for the current prisma model, and include or select all properties that are
377
+ required for the fields that could not be resolved without an additional query.
378
+
379
+ The following are some edge cases that could cause an additional query to be necessary:
380
+
381
+ - The parent object was not loaded through a field defined with `t.prismaField`, or `t.relation`
382
+ - The root `prismaField` did not correctly spread the `query` arguments in is prisma call.
383
+ - The query selects multiple fields that use the same relation with different queries
384
+ - The query contains multiple aliases for the same relation field with different arguments in a way
385
+ that results in different query options for the relation.
386
+ - A relation field has a query that is incompatible with the default includes of the parent object
387
+
388
+ All of the above should be relatively uncommon in normal usage, but the plugin ensures that these
389
+ types of edge cases are automatically handled when they do occur.
390
+
391
+ ### Without Find Unique
392
+
393
+ This is generally _NOT RECOMMENDED_, but you can set `findUnique` to null for some prisma objects.
394
+ Doing this will prevent the plugin from resolving queries for conflicting relations. Because of
395
+ this, you will need to provide a `resolve` method when defining relations, and some other options
396
+ (like field level selects, described below) will not be available. This `resolve` method is _ONLY
397
+ CALLED AS A FALLBACK_ when the relation has not already been loaded. This means that you should not
398
+ apply any sorting or filtering to the relation queried in the resolve method. Instead used the
399
+ `query` option described in the next section
400
+
401
+ ```typescript
402
+ builder.prismaObject('User', {
403
+ findUnique: null,
404
+ fields: (t) => ({
405
+ id: t.exposeID('id'),
406
+ posts: t.relation('posts', {
407
+ resolve: (query, user) =>
408
+ db.post.findMany({
409
+ ...query,
410
+ where: { authorId: user.id },
411
+ }),
412
+ }),
413
+ }),
512
414
  });
513
415
  ```
514
416
 
@@ -516,16 +418,15 @@ prisma.user.findUnique({
516
418
 
517
419
  So far we have been describing very simple queries without any arguments, filtering, or sorting. For
518
420
  `t.prismaField` definitions, you can add arguments to your field like normal, and pass them into
519
- your prisma query as needed. For `t.relation` the flow is slightly different because we need to make
520
- sure we are loading the right data if we are pre-loading data in a parent resolver. We do this by
521
- adding a `query` option to our field options.
421
+ your prisma query as needed. For `t.relation` the flow is slightly different because we are not
422
+ making a prisma query directly. We do this by adding a `query` option to our field options. Query
423
+ can either be a query object, or a method that returns a query object based on the field arguments.
522
424
 
523
425
  ```typescript
524
426
  builder.prismaObject('User', {
525
427
  findUnique: (user) => ({ id: user.id }),
526
428
  fields: (t) => ({
527
429
  id: t.exposeID('id'),
528
- email: t.exposeString('email'),
529
430
  posts: t.relation('posts', {
530
431
  // We can define arguments like any other field
531
432
  args: {
@@ -542,11 +443,11 @@ builder.prismaObject('User', {
542
443
  });
543
444
  ```
544
445
 
545
- This query will be part of the `query` that gets passed into the first argument of `resolve`
546
- function for `t.relation` and `t.prismaField` based fields, and include things like `where`, `skip`,
547
- `take`, `orderBy`, etc. The `query` function will be passed the arguments for the field, and the
548
- context for the current request. Because it is used for pre-loading data, and solving n+1 issues, it
549
- can not be passed the `parent` object because it may not be loaded yet.
446
+ The returned query object will be added to the include section of the `query` argument that gets
447
+ passed into the first argument of the parent `t.prismaField`, and can include things like `where`,
448
+ `skip`, `take`, abd `orderBy`. The `query` function will be passed the arguments for the field, and
449
+ the context for the current request. Because it is used for pre-loading data, and solving n+1
450
+ issues, it can not be passed the `parent` object because it may not be loaded yet.
550
451
 
551
452
  If your field has a `resolve` method the generated `query` will be passed in as part of the first
552
453
  arg to your resolve function
@@ -568,20 +469,37 @@ builder.prismaObject('User', {
568
469
  createdAt: args.oldestFirst ? 'asc' : 'desc',
569
470
  },
570
471
  }),
571
- // query here will contain the orderBy (and any other properties returned by the query method)
472
+ // optional: query here will contain the orderBy (and any other properties returned by the query method)
572
473
  resolve: (query, post) => db.post.findMany({ ...query, where: { id: post.authorId } }),
573
474
  }),
574
475
  }),
575
476
  });
576
477
  ```
577
478
 
578
- It is VERY IMPORTANT to put all your filtering and sorting into the query method rather than your
479
+ It is _VERY IMPORTANT_ to put all your filtering and sorting into the query method rather than your
579
480
  resolver because the resolver is only used as fallback, and any filtering that does not exist in the
580
481
  query method will not be applied correctly. If you have a where in both your query and your
581
482
  resolver, you will need to ensure these are merged correctly. It is generally better NOT to use a
582
483
  custom resolver.
583
484
 
584
- ### Includes on types
485
+ ## relationCount
486
+
487
+ Prisma supports querying for
488
+ [relation counts](https://www.prisma.io/docs/concepts/components/prisma-client/aggregation-grouping-summarizing#count-relations)
489
+ which allow including counts for relations along side other `includes`. This does not currently
490
+ support any filters on the counts, but can give a total count for a relation.
491
+
492
+ ```typescript
493
+ builder.prismaObject('User', {
494
+ findUnique: (user) => ({ id: user.id }),
495
+ fields: (t) => ({
496
+ id: t.exposeID('id'),
497
+ postCount: t.relationCount('posts'),
498
+ }),
499
+ });
500
+ ```
501
+
502
+ ## Includes on types
585
503
 
586
504
  In some cases, you may want to always pre-load certain relations. This can be helpful for defining
587
505
  fields directly on type where the underlying data may come from a related table.
@@ -606,89 +524,140 @@ builder.prismaObject('User', {
606
524
  });
607
525
  ```
608
526
 
609
- ### relationCount
527
+ ## Select mode for types
610
528
 
611
- Prisma supports querying for
612
- [relation counts](https://www.prisma.io/docs/concepts/components/prisma-client/aggregation-grouping-summarizing#count-relations)
613
- which allow including counts for relations along side other `includes`. This does not currently
614
- support any filters on the counts, but can give a total count for a relation.
529
+ By default, the prisma plugin will use `include` when including relations, or generating fallback
530
+ queries. This means we are always loading all columns of a table when loading it in a
531
+ `t.prismaField` or a `t.relation`. This is usually what we want, but in some cases, you may want to
532
+ select specific columns instead. This can be useful if you have tables with either a very large
533
+ number of columns, or specific columns with large payloads you want to avoid loading.
534
+
535
+ To do this, you can add a `select` instead of an include to your `prismaObject`:
615
536
 
616
537
  ```typescript
617
538
  builder.prismaObject('User', {
539
+ select: {
540
+ id: true,
541
+ },
618
542
  findUnique: (user) => ({ id: user.id }),
619
543
  fields: (t) => ({
620
544
  id: t.exposeID('id'),
621
- postCount: t.relationCount('posts'),
545
+ email: t.exposeString('email'),
622
546
  }),
623
547
  });
624
548
  ```
625
549
 
626
- ## Relay integration
550
+ At the very least, you will need to `select` the properties required by your `findUnique` function.
551
+ The `t.expose*` and `t.relation` methods will all automatically add selections for the exposed
552
+ fields _WHEN THEY ARE QUERIED_, ensuring that only the requested columns will be loaded from the
553
+ database.
627
554
 
628
- This plugin has extensive integration with the
629
- [relay plugin](https://pothos-graphql.dev/docs/plugins/relay), which makes creating nodes and
630
- connections very easy.
555
+ In addition to the `t.expose` and `t.relation`, you can also add custom selections to other fields:
631
556
 
632
- ### Example
557
+ ```typescript
558
+ builder.prismaObject('User', {
559
+ select: {
560
+ id: true,
561
+ },
562
+ findUnique: (user) => ({ id: user.id }),
563
+ fields: (t) => ({
564
+ id: t.exposeID('id'),
565
+ email: t.exposeString('email'),
566
+ bio: t.string({
567
+ // This will select user.profile.bio when the the `bio` field is queried
568
+ select: {
569
+ profile: {
570
+ select: {
571
+ bio: true,
572
+ },
573
+ },
574
+ },
575
+ resolve: (user) => user.profile.bio,
576
+ }),
577
+ }),
578
+ });
579
+ ```
580
+
581
+ ## Type variants
633
582
 
634
- The following example is similar to the one above with a few changes:
583
+ The prisma plugin supports defining multiple GraphQL types based on the same prisma model.
584
+ Additional types are called `variants`. You will always need to have a "Primary" variant (defined as
585
+ described above). Additional variants can be defined by providing a `variant` option instead of a
586
+ `name` option when creating the type:
635
587
 
636
- - the `User` and `Post` objects are now relay nodes
637
- - the `posts` field on the `User` type is now a relay connection using cursor based pagination
638
- - there is a new `users` query that is also a relay connection
588
+ ```typescript
589
+ const Viewer = builder.prismaObject('User', {
590
+ variant: 'Viewer',
591
+ findUnique: (user) => ({ id: user.id }),
592
+ fields: (t) => ({
593
+ id: t.exposeID('id'),
594
+ });
595
+ ```
639
596
 
640
- Everything in this schema is still queryable via a single prisma query. The relay connections
641
- handles pre-loading like all the other fields.
597
+ You can define variant fields that reference one variant from another:
642
598
 
643
599
  ```typescript
644
- builder.prismaNode('User', {
645
- findUnique: (id) => ({ id }),
646
- id: { resolve: (user) => user.id },
600
+ const Viewer = builder.prismaObject('User', {
601
+ variant: 'Viewer',
602
+ findUnique: (user) => ({ id: user.id }),
603
+ fields: (t) => ({
604
+ id: t.exposeID('id'),
605
+ // Using the model name ('User') will reference the primary variant
606
+ user: t.variant('User'),
607
+ });
608
+ });
609
+
610
+ const User = builder.prismaNode('User', {
611
+ // Testing that user is typed correctly
612
+ authScopes: (user) => !!user.id,
613
+ interfaces: [Named],
614
+ id: {
615
+ resolve: (user) => user.id,
616
+ },
647
617
  fields: (t) => ({
618
+ // To reference another variant, use the returned object Ref instead of the model name:
619
+ viewer: t.variant(Viewer, {}),
648
620
  email: t.exposeString('email'),
649
- posts: t.relatedConnection('posts', {
650
- cursor: 'id',
651
- args: {
652
- oldestFirst: t.arg.boolean(),
653
- },
654
- query: (args, context) => ({
655
- orderBy: {
656
- createdAt: args.oldestFirst ? 'asc' : 'desc',
657
- },
658
- }),
659
- }),
660
621
  }),
661
622
  });
623
+ ```
662
624
 
663
- builder.prismaNode('Post', {
625
+ You can also use variants when defining relations by providing a `type` option:
626
+
627
+ ```typescript
628
+ const PostDraft = builder.prismaNode('Post', {
629
+ variant: 'PostDraft'
630
+ // This is used to load the node by id
664
631
  findUnique: (id) => ({ id }),
632
+ // This is used to get the id from a node
665
633
  id: { resolve: (post) => post.id },
634
+ // fields work just like they do for builder.prismaObject
666
635
  fields: (t) => ({
667
636
  title: t.exposeString('title'),
668
637
  author: t.relation('author'),
669
638
  }),
670
639
  });
671
640
 
672
- builder.queryType({
641
+ const Viewer = builder.prismaObject('User', {
642
+ variant: 'Viewer',
643
+ findUnique: (user) => ({ id: user.id }),
673
644
  fields: (t) => ({
674
- me: t.prismaField({
675
- type: 'User',
676
- resolve: async (query, root, args, ctx, info) =>
677
- prisma.user.findUnique({
678
- ...query,
679
- rejectOnNotFound: true,
680
- where: { id: ctx.userId },
681
- }),
682
- }),
683
- posts: t.prismaConnection({
684
- type: 'Post',
685
- cursor: 'id',
686
- resolve: (query) => prisma.post.findMany(query),
645
+ id: t.exposeID('id'),
646
+ drafts: t.relation('posts', {
647
+ // This will cause this relation to use the PostDraft variant rather than the default Post variant
648
+ type: PostDraft,
649
+ query: { where: { draft: true } },
687
650
  }),
688
- }),
651
+ });
689
652
  });
690
653
  ```
691
654
 
655
+ ## Relay integration
656
+
657
+ This plugin has extensive integration with the
658
+ [relay plugin](https://pothos-graphql.dev/docs/plugins/relay), which makes creating nodes and
659
+ connections very easy.
660
+
692
661
  ### `prismaNode`
693
662
 
694
663
  The `prismaNode` method works just like the `prismaObject` method with a couple of small
@@ -746,6 +715,9 @@ builder.queryType({
746
715
  connection. The `query` will contain the correct `take`, `skip`, and `cursor` options based on the
747
716
  connection arguments (`before`, `after`, `first`, `last`), along with `include` options for nested
748
717
  selections.
718
+ - `totalCount`: A function for loading the total count for the connection. This will add a
719
+ `totalCount` field to the connection object. The `totalCount` method will receive (`connection`,
720
+ `args`, `context`, `info`) as arguments
749
721
 
750
722
  The created connection queries currently support the following combinations of connection arguments:
751
723
 
@@ -799,6 +771,8 @@ builder.prismaNode('User', {
799
771
  passed to prisma.
800
772
  - `defaultSize`: (default: 20) The default page size to use if `first` and `last` are not provided.
801
773
  - `maxSize`: (default: 100) The maximum number of nodes returned for a connection.
774
+ - `query`: A method that accepts the `args` and `context` for the connection field, and returns
775
+ filtering and sorting logic that will be merged into the query for the relation.
802
776
  - `resolve`: (optional) Used as a fallback when a connection is not pre-loaded. It is optional, and
803
777
  generally should NOT be defined manually. If used it works like a combination of the `resolve`
804
778
  method of `relation` and `prismaConnection`. The default will use the `findUnique` of the current
@@ -806,3 +780,134 @@ builder.prismaNode('User', {
806
780
  relationships to improve query efficiency.
807
781
  - `totalCount`: when set to true, this will add a `totalCount` field to the connection object. see
808
782
  `relationCount` above for more details.
783
+
784
+ ## Using Prisma without a plugin
785
+
786
+ Using prisma without a plugin is relatively straight forward using the `builder.objectRef` method.
787
+
788
+ The easiest way to create types backed by prisma looks something like:
789
+
790
+ ```typescript
791
+ import { Post, PrismaClient, User } from '@prisma/client';
792
+
793
+ const db = new PrismaClient();
794
+ const UserObject = builder.objectRef<User>('User');
795
+ const PostObject = builder.objectRef<Post>('Post');
796
+
797
+ UserObject.implement({
798
+ fields: (t) => ({
799
+ id: t.exposeID('id'),
800
+ email: t.exposeString('email'),
801
+ posts: t.field({
802
+ type: [PostObject],
803
+ resolve: (user) =>
804
+ db.post.findMany({
805
+ where: { authorId: user.id },
806
+ }),
807
+ }),
808
+ }),
809
+ });
810
+
811
+ PostObject.implement({
812
+ fields: (t) => ({
813
+ id: t.exposeID('id'),
814
+ title: t.exposeString('title'),
815
+ author: t.field({
816
+ type: UserObject,
817
+ resolve: (post) =>
818
+ db.user.findUnique({ rejectOnNotFound: true, where: { id: post.authorId } }),
819
+ }),
820
+ }),
821
+ });
822
+
823
+ builder.queryType({
824
+ fields: (t) => ({
825
+ me: t.field({
826
+ type: UserObject,
827
+ resolve: (root, args, ctx) =>
828
+ db.user.findUnique({ rejectOnNotFound: true, where: { id: ctx.userId } }),
829
+ }),
830
+ }),
831
+ });
832
+ ```
833
+
834
+ This sets up User, and Post objects with a few fields, and a `me` query that returns the current
835
+ user. There are a few things to note in this setup:
836
+
837
+ 1. We split up the `builder.objectRef` and the `implement` calls, rather than calling
838
+ `builder.objectRef(...).implement(...)`. This prevents typescript from getting tripped up by the
839
+ circular references between posts and users.
840
+ 2. We use rejectOnNotFound with our `findUnique` calls because those fields are not nullable.
841
+ Without this option, prisma will return a null if the object is not found. An alternative is to
842
+ mark these fields as nullable.
843
+ 3. The refs to our object types are called `UserObject` and `PostObject`, this is because `User` and
844
+ `Post` are the names of the types imported from prisma. We could instead alias the types when we
845
+ import them so we can name the refs to our GraphQL types after the models.
846
+
847
+ This setup is fairly simple, but it is easy to see the n+1 issues we might run into. Prisma helps
848
+ with this by batching queries together, but there are also things we can do in our implementation to
849
+ improve things.
850
+
851
+ One thing we could do if we know we will usually be loading the author any time we load a post is to
852
+ make the author part of shape required for a post:
853
+
854
+ ```typescript
855
+ const UserObject = builder.objectRef<User>('User');
856
+ // We add the author here in the objectRef
857
+ const PostObject = builder.objectRef<Post & { author: User }>('Post');
858
+
859
+ UserObject.implement({
860
+ fields: (t) => ({
861
+ id: t.exposeID('id'),
862
+ email: t.exposeString('email'),
863
+ posts: t.field({
864
+ type: [PostObject],
865
+ resolve: (user) =>
866
+ db.post.findMany({
867
+ // We now need to include the author when we query for posts
868
+ include: {
869
+ author: true,
870
+ },
871
+ where: { authorId: user.id },
872
+ }),
873
+ }),
874
+ }),
875
+ });
876
+
877
+ PostObject.implement({
878
+ fields: (t) => ({
879
+ id: t.exposeID('id'),
880
+ title: t.exposeString('title'),
881
+ author: t.field({
882
+ type: UserObject,
883
+ // Now we can just return the author from the post instead of querying for it
884
+ resolve: (post) => post.author,
885
+ }),
886
+ }),
887
+ });
888
+ ```
889
+
890
+ We may not always want to query for the author though, so we could make the author optional and fall
891
+ back to using a query if it was not provided by the parent resolver:
892
+
893
+ ```typescript
894
+ const PostObject = builder.objectRef<Post & { author?: User }>('Post');
895
+
896
+ PostObject.implement({
897
+ fields: (t) => ({
898
+ id: t.exposeID('id'),
899
+ title: t.exposeString('title'),
900
+ author: t.field({
901
+ type: UserObject,
902
+ resolve: (post) =>
903
+ post.author ?? db.user.findUnique({ rejectOnNotFound: true, where: { id: post.authorId } }),
904
+ }),
905
+ }),
906
+ });
907
+ ```
908
+
909
+ With this setup, a parent resolver has the option to include the author, but we have a fallback
910
+ incase it does not.
911
+
912
+ There are other patterns like dataloaders than can be used to reduce n+1 issues, and make your graph
913
+ more efficient, but they are too complex to describe here.