-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
[RFC] GraphQL adaptators improvements #2243
Comments
Hi @Weakky, thanks a lot for your feedback. The truth is, we don't have much hindsights on the graphql providers yet.
Indeed. We opted for this in order to get a proof of concept for graphql providers. There is room for improvements!
I don't think we should as it would defeat another benefit of graphql, only fetching what you need. We can't make that decision for you
It is already. In fact, you can disable the introspection altogether and provide queries for each requests of each resources. One of our partners is actually doing it. Overly simplifying, they use a dataProvider such as: import buildGraphQLProvider from 'ra-data-graphql'
const buildQuery = (raFetchType, resourceName, params) => {
switch (raFetchType) {
case GET_LIST: {
switch (resourceName) {
case 'Comments': {
return {
gql: gql`query Comments ($pagination: PaginationInput!, $sort: SortInput!, $filter: ContenuFilterInput){
data: comments (pagination: $pagination, sort: $sort, filter: $filter){
data {
id
body
}
total
}
}`,
variables: params,
parseResponse: response => response.data
}
}
}
}
}
};
buildGraphQLProvider({
client: apolloClient,
buildQuery,
introspection: false
}); |
The way I see it, you should use the introspection enabled dataProvider for prototyping, switching progressively and selectively to hand crafted queries when needed |
I agree with @Weakky that overriding the entire query just to add a related resource might come in costly. We have to find something easier. By the way, the |
What |
react-admin/examples/graphcool-demo/src/dataProvider.js Lines 35 to 49 in b36bc9d
|
Thanks for the fast response !
Invalid argument, you're batching all
Totally agree with you here. Detect the hot-spots, and override these queries by handcrafted one when needed. Hence my suggestion to simplify the current API to override the queries, so that it can be done in a more concise way ! |
Propositions:
What do you think ? |
Apologies but I'm not sure to understand how we'd be able to decorate the Let's assume we're talking about react-admin/examples/graphcool-demo/src/dataProvider.js Lines 4 to 49 in b36bc9d
|
No problem, I should have taken more time to think about it in the first place :) There are two ways I think:
// in src/buildDataProvider.js
import buildApolloClient from 'ra-data-graphcool';
export default () =>
buildApolloClient({
clientOptions: {
uri: 'https://api.graph.cool/simple/v1/cj2kl5gbc8w7a0130p3n4eg78',
},
override: {
Command: {
GET_ONE: (params) => ({
query: gql`...`, // You could get syntax highlighting here depending on your IDE
// Optionally provide variables if you need to work on them for this specific resource/fetchType
variables: params,
// Optionally provide parseResponse if you need to work on it for this specific resource/fetchType
parseResponse: response => response.data
}),
// Or
GET_ONE: gql`...`, // You could get syntax highlighting here depending on your IDE
// Or
GET_ONE: `...`, // No syntax highlighting here as this is a simple string :(
},
},
});
// in src/buildDataProvider.js
import buildApolloClient, { buildQuery } from 'ra-data-graphcool';
const myBuildQuery = buildQuery => (aorFetchType, resource, params) => {
const builtQuery = buildQuery(aorFetchType, resource, params);
if (resource === 'Category' && aorFetchType === 'GET_ONE') {
return {
...builtQuery,
query: gql`...`, // Or a string directly
};
}
};
export default () =>
buildApolloClient({
clientOptions: {
uri: 'https://api.graph.cool/simple/v1/cj2kl5gbc8w7a0130p3n4eg78',
},
buildQuery: myBuildQuery(buildQuery),
}); I personally prefer the second option |
Thanks for the examples, it's much clearer now. I prefer the second option as well. It'd allow us to mimic the One thing though, unless I missed something, I don't get how the final query will be crafted in case we pass a simple string ? Besides, even if we add some kind of query processing in The way I saw it, although I'd prefer decorating the react-admin/packages/ra-data-graphcool/src/buildGqlQuery.js Lines 96 to 104 in b36bc9d
I may be wrong here though 🤔 |
No, you missed this part (I added comments to make it clearer): return {
...builtQuery, // Merge the default builtQuery (variables and parseResponse)
query: gql`...`, // Or a string directly
}; It also means that |
I think you missed what I meant as well, let me rephrase it 😛. I get that the only param overriden on your example will be the Here's what a valid query shops(
$where: ShopWhereInput
$orderBy: ShopOrderByInput
$skip: Int
$first: Int
) {
items: shops(where: $where, orderBy: $orderBy, skip: $skip, first: $first) {
id
name
address
}
total: shopsConnection(
where: $where
orderBy: $orderBy
skip: $skip
first: $first
) {
aggregate {
count
}
}
} If we override the return {
...builtQuery,
query: `{ id name address }` //Same API as GraphQL Bindings
} All the Now, I totally get your point regarding the loose of syntax highlight. |
I think we were just not on the same line from the beginning. If the goal of using a simple string is only to remove the use of Here's what you might be able to achieve using GraphQL Bindings): // Retrieve `firstName` and `lastName` of a specific user
binding.query.user({ where: { id: 'user_id' } }, '{ firstName lastName }'); (Bindings either accept the query AST passed in all resolvers (known as the This would produce the following GraphQL query: {
user(where: { id: 'user_id' }) {
firstName
lastName
}
} Think about it as a GraphQL In the example above, In a nutshell, the 'simple string' option would be a way to only express the fields user wants to fetch, removing all the If you do agree with me now (and I'd understand if you didn't like it at all 😞), best-case scenario would be to allow all three options like you proposed before. |
Those are the standard parameters of the query matching the As much as I personally like the graphql bindings syntax, I fail to see what you'd like |
Yes they're, but prisma isn't relevant here. I just wanted to highlight
Indeed, but on your proposition, we're overriding the whole query, meaning passing a simple GraphQL fragment as shown above won't work because the
My initial statement is about GraphQL providers in general, and not specifically about Proposition
What do you think ? |
I think I'm really lost now 😆
|
I just roughly implemented the query overriding by fragments on a branch on |
Ok, I think I get what you're doing now. Basically, you want a way to override a query without having to write the I think the // in src/buildDataProvider.js
import buildApolloClient, { buildQuery } from 'ra-data-graphcool';
const productGetListQuery = gql`
fragment product on Product {
id
name
description
brand {
id
name
}
category {
id
name
}
shop {
id
name
}
attributes {
id
value
}
}
`;
const myBuildQuery = buildQuery => (aorFetchType, resource, params) => {
if (resource === 'Category' && aorFetchType === 'GET_ONE') {
// buildQuery accepts an extra parameter which is the query/fragment to use as an override
// The rest of your logic still applies but you wont have to find the override
return buildQuery(aorFetchType, resource, params, productGetListQuery);
}
return buildQuery(aorFetchType, resource, params);
};
export default () =>
buildApolloClient({
clientOptions: {
uri: 'https://api.graph.cool/simple/v1/cj2kl5gbc8w7a0130p3n4eg78',
},
buildQuery: myBuildQuery(buildQuery),
}); Besides, I assume the only thing we have to do on our side is to include the full schema in the |
You should've seen my smile reading those sweet words 😂
This is fine for me. Your way of doing it is more consistent as you're planning to remove the
Exactly ! Let me recap everything:
I might find some time to send a few PR's 🎉 |
Closing this issue as the first part has been merged and will be available in |
Hey there,
As of now, most GraphQL adaptators aren't taking full advantage of one of GraphQL's biggest benefit: being able to query nested fields in one request, as deep as the schema allow us to.
Instead, they're more or less imitating the way REST works, by making X request for X reference needed (although some great optimizations were already done in that regard https://marmelab.com/blog/2016/10/18/using-redux-saga-to-deduplicate-and-group-actions.html)
I think there are ways we could improve that, and I suggest we use this place as a central thread to discuss about what could be done.
Suggestion n°1
Include all scalar fields of linked types by default
As of now, we're only fetching ids from linked types, which forces us to use
<ReferenceField />
on every linked type.One quick-fix that could be done, is to automatically fetch all scalar fields of linked types:
This way, we're able to use
<TextField source="linkedType.scalarField" />
, which already cover lots of cases and greatly improve the amount of request made.I think the few more bytes that those additional fields will take on every requests are more than worth the amount of request that it will save.
Suggestion n°2
Make this overridable
After thinking about it, @fzaninotto, I don't think there's a need for a function to transform the
introspectionResults
. The introspection query already fetches all the existing types (and their fields) anyway.If I'm not mistaking, overriding the behavior described above is actually already possible and that's what you've done here, by overriding the query that fetches the
Command
.I think we could provide a simpler and more concise API to do that, for two reasons:
The query name and the params are not needed for what we're trying to achieve. The params will always have to be declared anyway, and it can become heavy when overriding
GET_LIST
actions (having filters, sorting and pagination as input).Users have to be careful about the aliases (
data
and sometimestotal
)If you agree with me, I see two ways of enhancing this:
graphqlify
expects (more consistent, but heavier IMO)That's all for now, tell me what you guys think 🙌
The text was updated successfully, but these errors were encountered: