API suggestions #476
Replies: 1 comment 2 replies
-
Yes, the changes you are suggesting look a little cleaner for some use cases, but make a lot of common, but slightly more advanced patterns impossible. Some of this comes down to type-safety/inference, some of it comes down to how plugins work. There is also a lot of complexity hidden in how pothos lazy-loads the field functions that helps avoid common problems with circular references, circular imports and other nasty side effects of trying to define a graph with circular relations. Some more concrete examples: export const CommentObject = builder
.objectRef<Comment>('Comment')
.implement({
fields: {
id: exposeID('id'),
},
)} In this example, export const getPost = (t: PothosT) => t.field({
type: PostObject,
nullable: true,
args: {
id: t.arg.id({ required: true }),
},
resolve: (root, args) => [...Posts.values()]
.find(post => post.id === String(args.id)),
}) The better alternative here is to do something like: builder.objectField(PostObject, 'getPost', t => t.field({
type: PostObject,
nullable: true,
args: {
id: t.arg.id({ required: true }),
},
resolve: (root, args) => [...Posts.values()]
.find(post => post.id === String(args.id)),
})) Builder pattern vs composition import * as userObjects from './users/objects'
import * as postObjects from './posts/objects'
import * as userQueries from './users/queries'
import * as postQueries from './posts/queries'
// note, you just have the schema without having to call `toSchema` because there's no necessary mutability on `builder`.
export const schema = new SchemaBuilder({
plugins: [...],
objects: {
...userObjects,
...postObjects,
},
query: {
...postsQueries,
...usersQueries,
},
}); This is closer to how the original versions of Pothos worked. This works okay at small scales, but for large graphs this becomes a pain to manage. I also know from experience remembering to import and add the right types to the right places is something many skilled developers still struggled with, and * imports also cause a lot of issues with people accidentally exporting something other than builder types. Whether or not a builder pattern vs a composition pattern is better is probably a large question, and might get into very opinionated territory. More practically speaking, there are a lot of good use cases for taking the same schema definitions and building them with different options. There are a few plugins that take advantage of this (mocks, sub-graph, federation, auth). Using a builder pattern allows having all the definitions created once, and then creating schemas with different transforms, features, or filters applied. This can also be achieved in other ways, but over all, based in the feedback I got on early versions of Pothos using the current pattern of creating objects through the builder, and removing the need to import all schema types was a very posative change. Another big reason the pattern suggested above would not work is that the API, and features of the builder change drastically based on options passed in when creating the builder. The builder carries type information for all kinds of things from types for custom scalars, to plugin features like authorization, to resolver contexts, etc. All of this type information, and all the other settings provided to the builder would need to somehow be provided to all the other files that define schema types. |
Beta Was this translation helpful? Give feedback.
-
I have a hunch that the choices made, which I'm about to question, came with tradeoffs regarding type inference and brevity (a "less brevity here, more brevity there" type of thing).
callback pattern on
fields
I'm curious why you went with the callback pattern. Either to cut down on imports or to allow something dynamic on the value of
t
, I'm guessing.For instance, using an objectRef definition as example:
rather than a plain object pattern:
This is not a huge deal, but it makes the code more cluttered in my opinion.
This is more noticeable when developing resolvers as independent variables:
which have to be imported and called like so:
It would be nice to be able to just do this:
sidenote:
Nexus
uses a callback pattern too, but their API is more indirect, making this necessary. In the below example, you don't even have keys on thedefinition
(in pothos, this would befields
essentially), so you have to callt.string
and othert
methods just to register a field with a key. Not a fan of how they do this at all.extending builder vs importing to builder
I'm curious what the pros and cons are here. To me, extending
builder
via dot methods likeobjectRef
is less clean than importing to a
builder
function.I'm guessing this suggestion might make certain TypeScript things harder to do (maybe impossible), but that's without digging too deeply. Curious to hear your thoughts.
Beta Was this translation helpful? Give feedback.
All reactions