-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GraphQL default schema issues and potential for customisation #5777
Comments
First of all, I'd like to thank you for trying the brand new GraphQL API, do this detailed analysis and share your feedback. You are very welcome to contribute to the project in discussions and PRs. The current version of the GraphQL API is just what we considered as the minimum to run a GraphQL API on top of Parse Server, so we could get feedback from the community (like yours) and continue working. There is still a lot that can be done to leverage all the GraphQL potential into Parse Server project. We created a first short to do list here. Feel free to suggest new tasks and/or work in any of them. I agree with most of your points. Let me comment each of them (maybe we could create a separate issue/task for each of them):
Again, thanks for your help. Let's keep the discussion for each of the items and tackle them as a team! |
Hi @davimacedo - I'd be happy to join the project and start ticking off some of the items on your todo list. I agree about creating separate tasks for some of the issues above; overall I'm just pleased to know that you have a plan in place already and it seems that you already have considered most of the concerns I've highlighted! Plug-and-play Schema Overload Nested Mutations: mutation SyncExecutionTest {
first: objects {
createGraphTest(fields: { name: "Numero Uno", delay: 3000 }) {
createdAt
}
}
second: objects {
createGraphTest(fields: { name: "Numero Dos" }) {
createdAt
}
}
} {
"data": {
"first": {
"createGraphTest": {
"createdAt": "2019-07-07T11:24:56.951Z"
}
},
"second": {
"createGraphTest": {
"createdAt": "2019-07-07T11:25:00.123Z"
}
}
}
} Logs:
Cloud Functions: Hope this helps, I'm happy to get started on the Schema Overload issue as soon as possible. |
@omairvaiyani we are very happy to have you on board! Please find my comments below.
I assigned the task to you and I will discuss it further in your PR
I've seen this discussion about namespaces and that's why I've decided to fo with the nested mutations. So, in your opinion, should we keep the current approach?
I'd love to have both options:
|
I think let's keep the nesting as it's the lesser of two evils. Introducing 100+ mutations and queries all in one namespace will be a deterrent for end users trying to construct queries. Without nesting, we would also be forced to share the space between the base types and any additional types from schema stitching future. The workaround for atomicity issue will do fine for those that need it.
Given how flexible our upcoming Parse GraphQL Config is, I don't see any reason why we can't have both! A generic mutation should definitely be set as a default, just as we set the generic |
Nice! It sounds we have a plan! Talking about the function names, I'd prefer to validate the name and, in the case it does not pass, we can just log a warn and do not generate the mutation for it. It would have to be called using the generic mutation then. We could additionally have some way to enter the GraphQL mutation name. But these details I think we can discuss further when tackling this feature. |
This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions. Therefore, regarding #5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with #5782 and #5818, when merged, closes the issue. How it works: 1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one: ``` parseGraphQLServer = new ParseGraphQLServer(parseServer, { graphQLPath: '/graphql', graphQLCustomTypeDefs: gql` extend type Query { custom: Custom @namespace } type Custom { hello: String @resolve hello2: String @resolve(to: "hello") userEcho(user: _UserFields!): _UserClass! @resolve } `, }); ``` Note: - This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object); - This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The `to` argument specifies the function name. If the `to` argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field; - This PR allows creating custom types using the auto-generated ones as in `userEcho(user: _UserFields!): _UserClass! @resolve`; - This PR allows to extend the auto-generated types, as in `extend type Query { ... }`. 2. Once the schema was set, you just need to write regular cloud code functions: ``` Parse.Cloud.define('hello', async () => { return 'Hello world!'; }); Parse.Cloud.define('userEcho', async req => { return req.params.user; }); ``` 3. Now you are ready to play with your new custom api: ``` query { custom { hello hello2 userEcho(user: { username: "somefolk" }) { username } } } ``` should return ``` { "data": { "custom": { "hello": "Hello world!", "hello2": "Hello world!", "userEcho": { "username": "somefolk" } } } } ```
@omairvaiyani do you think that we've already addressed everything here and we can close this issue? |
Yup, very quick turnaround I'd say! |
This PR empowers the Parse GraphQL API with custom user-defined schema. The developers can now write their own types, queries, and mutations, which will merged with the ones that are automatically generated. The new types are resolved by the application's cloud code functions. Therefore, regarding parse-community#5777, this PR closes the cloud functions needs and also addresses the graphql customization topic. In my view, I think that this PR, together with parse-community#5782 and parse-community#5818, when merged, closes the issue. How it works: 1. When initializing ParseGraphQLServer, now the developer can pass a custom schema that will be merged to the auto-generated one: ``` parseGraphQLServer = new ParseGraphQLServer(parseServer, { graphQLPath: '/graphql', graphQLCustomTypeDefs: gql` extend type Query { custom: Custom @namespace } type Custom { hello: String @resolve hello2: String @resolve(to: "hello") userEcho(user: _UserFields!): _UserClass! @resolve } `, }); ``` Note: - This PR includes a @namespace directive that can be used to the top level field of the nested queries and mutations (it basically just returns an empty object); - This PR includes a @resolve directive that can be used to notify the Parse GraphQL Server to resolve that field using a cloud code function. The `to` argument specifies the function name. If the `to` argument is not passed, the Parse GraphQL Server will look for a function with the same name of the field; - This PR allows creating custom types using the auto-generated ones as in `userEcho(user: _UserFields!): _UserClass! @resolve`; - This PR allows to extend the auto-generated types, as in `extend type Query { ... }`. 2. Once the schema was set, you just need to write regular cloud code functions: ``` Parse.Cloud.define('hello', async () => { return 'Hello world!'; }); Parse.Cloud.define('userEcho', async req => { return req.params.user; }); ``` 3. Now you are ready to play with your new custom api: ``` query { custom { hello hello2 userEcho(user: { username: "somefolk" }) { username } } } ``` should return ``` { "data": { "custom": { "hello": "Hello world!", "hello2": "Hello world!", "userEcho": { "username": "somefolk" } } } } ```
I'd first like to congratulate the core contributors for an excellent job at introducing GraphQL into Parse. It really is a big step forward at keeping the Parse project modern and exciting for new developers.
I can see that the approach has been one of plug-n-play; all types, queries and mutations are automatically generated which means that for many teams, they can swap out many of their existing queries with GraphQL. This is pretty amazing stuff - but it does have inherent tradeoffs. I'll list these concerns out in hopes of some productive discussion to guide future development before the community's usage of the ParseGraphQLServer becomes too reliant on the status quo.
Schema overload
Autogenerating the graph using the entire database schema, and adding all default mutations for each class is like bending GraphQL into an exact map of what Parse already offers. The real power of GraphQL is the ability to generate representations of your database that you would like to make available to your clients, incrementally and evolutionarily. Its an opportunity for enterprises and scaling teams to re-think their schema, and decide what really should be accessed and mutated.
I think an ability to configure the Parse GraphQL server should be introduced that does one or two of the following:
Now, we don't want to throw the baby out with the bathwater here. There is some impressive coding done here by the contributors around the use of resolvers and mapping of GraphQL query constraints with that of Parse. Careful consideration is needed to maintain these helpful modules whilst allowing developers to extend the ParseGraphQLServer to their specific needs.
Nested mutations lose atomicity
Mutations differ from queries in that they run in series, rather than in parallel. This is to allow atomic mutations. Unfortunately, nesting mutations as is the case with the Parse implementation, loses this synchronous execution and results in tradeoff of atomic mutations.
As it currently stands, Parse's GraphQL Schema nests all mutations under
objects
(andusers
) like so:No obvious location for Cloud Functions
CloudFunctions don't really have a home in mutations as it stands. This means that clients will have to maintain two separate network requests layers should they require full-usage of a parse server API. Whilst we are striving to reduce our reliance of RPC's, there are many instances where an RPC approach is required, and so we'd very much like CloudFunctions to be first-class citizens in ParseGraphQLServer.
--
I was tempted to begin forking and adding PRs to establish some of the above suggestions, but I think it's absolutely critical that some discussion is done before further code is cooked.
The text was updated successfully, but these errors were encountered: