-
Notifications
You must be signed in to change notification settings - Fork 134
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: Support for multiple queries in Faust #1628
Comments
Thanks for writing this up @blakewilson. I've got a few questions before I offer my thoughts. In the case where a user specifies multiple |
Hey @JEverhart383, thanks for bringing this up! The data resolved from {
"4dd03f403403d6b001b160716df5990e53bafa4da78273d75d9cb2e65c622b0e": {
"generalSettings": {
...
},
"headerMenuItems": {
...
},
"footerMenuItems": {
...
}
},
"641f71bbfe48f0b065b5512d95398cf0d295538dc15b8fc519bf8232c573098f": {
"post": {
...
}
}
} The |
@blakewilson I think each query in the array of queries should specify it's own variables instead of use shared variables. Consider 2 root queries that are imported to the template: query GetRecentPosts( $first: Int ) {
posts( first: $first ) {
nodes {
id
title
}
}
} query GetMenuItems( $first: Int ) {
menuItems( first: $first ) {
nodes {
id
label
parentDatabaseId
}
}
} I would like to pass a different This is just one example, but I could probably come up with a lot more examples where shared variables could/would lead to unexpected bugs. I think the array of queries should look more like: Component.queries = [
{
query: GET_POST_QUERY,
variables: ({ databaseId }, ctx) => { ... },
possibleFutureContextToSendToWPGraphQLForThingsLikePersonalization: () => {}
},
{
query: GET_TEMPLATE_QUERY,
variables: ({ databaseId }, ctx) => { ... },
possibleFutureContextToSendToWPGraphQLForThingsLikePersonalization: () => {}
}
] This would allow for components to have a bit more control over their behavior at their own component level. |
I accidentally clicked "close with comment" instead of "comment" 🤦🏻♂️. re-opened. |
Thanks for the clarification @blakewilson. Historically, the use of an additional hook call to resolve data has confused more people than accessing it directly via This may not be the place for it, but given all the inflight work on app router and the template hierarchy, I think it's worth calling this out here. It's clear that one large query has downsides to multiple smaller queries, but I think this has a lot to do with how the template system works currently. The frustration I hear most often from users is some variation of this question from Discord: What is the easiest way of implementing header and footer that will be shared among all pages? Right now, most people end up reimplementing a header and footer component inside of each template, which means that its data requirements are either expressed in that template's query or passed in via props. The more I play around with RSCs, I wonder if there is a better way to organize these site parts so that my template's data requirements don't include "global" data as much as possible. When I think about traditional WP PHP templating, But I think it's worth talking about here because of how this nuance of the current template model manifests itself in user feedback. It's rarely, "I want the be able to have multiple queries", it's that "I want some way to avoid repeating 'global' site components" which is ultimately why we need multiple queries. FWIW, allowing multiple queries is the best way for us to improve the current thing, so I'm 100% on board with that addition to the API. |
I like this approach. It should work with our existing implementation. I'm going to change the original RFC post to reflect support for variables. |
@JEverhart383 I think allowing both
I think the I do think the implementation in App Router inherently looks different just because of the differences in template hierarchy between current Faust and App Router Faust. In App Router, you could have multiple React components make up a given UI ( |
Just want to clarify this statement and maybe my original comment:
Agreed, and I can't see a reason to use both so I might have chosen a bad example, just confirming that nobody using |
Yes! Totally correct @JEverhart383, this will be an additive feature as you say, so no breaking changes. |
Some notes:
|
I was thinking about this as well @theodesp, but didn't include it in the RFC so I'm glad you brought this up. Personally, I think if one query fails, they should all fail, just due to the nature of a Faust template. In my mind partial data isn't really acceptable, but there may be a use case I'm not thinking of. Any thoughts on this? @JEverhart383 @jasonbahl |
I don't know enough about RSC, so bringing this up just in case: In current versions of Faust, the query/fragment colocation pattern often interferes with code splitting (the component can't be loaded dynamically, since the gql exists as a property on it). Will this same issue persist with server components? Would be good to know before we literally ( 😝) double down on the pattern. |
Good question @justlevine, this will not persist to RSC (and app router support). Our app router support will likely not include Faust Templates as we know them today due to the nature of RSCs and how layouts work within Next.js App Router. |
I am not super familiar but I think it would be a cool optional addition. How would Also, would users be able to configure the time interval in which queries are batched together? |
I can actually verify that query/fragment colocation pattern only works with client components used in the client side. If you attempt to access a query/fragment client component via a server component you will get this error:
I'm working on a prototype implementation in this RFC to propose a structure that is compatible with RSC components |
@dgallardox These queries would not be batched together, but be requested via a export default function Component(props) {}
Component.queries = [
{
query: GET_POST_QUERY,
variables: ({ databaseId }, ctx) => {
return {
databaseId,
asPreview: ctx?.asPreview,
};
},
},
{
query: GET_TEMPLATE_QUERY,
// Variables are not required
variables: () => {},
apolloQueryOptions: {
context: {
fetchOptions: {
method: 'POST',
},
},
}
},
]; |
Hey @blakewilson if we are using |
@theodesp 👍 Added this to the drawbacks section. |
Love this RFC and the ensuing conversation. Have we considered allowing users to specify which client to use? I know Apollo suggests that a best practice is to separate queries that are dynamic based on user from those that are global. It would probably help cache hit performances if we allow this by letting devs specify which client to use. This feature could also support use cases where they have a primary WP client in addition to a client for another GraphQL endpoint (e.g. Shopify). Thoughts? |
Anything that makes Faust less coupled to its ApolloClient implementation (or makes that implementation more extendable) gets my vote |
Since this feature is merged already in this PR I will be closing this RFC soon. |
@theodesp - @josephfusco and I tried to test this out and ran into issues. We messaged @blakewilson but he was OOO so waiting to chat with him to get more insight and hopefully get some feedback on the implementation |
@jasonbahl thanks. I will keep it open then until those issues are resolved. |
What happens if the same query is used with different variables? Let's say I had a template that had various sections of posts (like a newspaper homepage) And I had several components that fetched a list of posts. And I had the following queries on my template: i.e. HomePageTemplate.queries = [
{
query: GET_POSTS,
variables: {
category: "featured"
},
query: GET_POSTS,
variables: {
category: "sports"
},
query: GET_POSTS,
variables: {
category: "news"
},
}
]; If the data is hashed on the query alone, then all these sections would have the same cached results. I could see this happen for templates that have multiple nav menus too. Like what if I had: const HomePageTemplate = () => {
return (
<>
<HeaderMenu />
<Content />
<FooterMenu />
</>
)
}
HomePageTemplate.queries = [
{
query: NAV_MENU_QUERY,
variables: { menu: "primary" }
},
{
query: FOOTER_MENU_QUERY,
variables: { menu: "footer" }
},
{
query: CONTENT_QUERY,
variables: ({uri}) => ({uri})
}
] Both the Header and Footer menu would have the same data returned from |
@blakewilson I think similar to what I suggested here: #1628 (comment) Each query might have different params associated with it that make it unique, and perhaps the cache key should be a hash of the entire object that makes a query a query. So instead of a hash of just the query, it would be a hash of the Query, Variables and any other keys passed to the object. i.e. hash( { query: MY_QUERY, variables: { ... }, myFuturePersonalizationIdentifier: { ...}, extensions: { locale: "en_US" } ) etc Then for useFaustQuery( {
{
query: MY_QUERY, variables: { ... },
myFuturePersonalizationIdentifier: { ...},
extensions: { locale: "en_US" }
}
) 🤷🏻♂️ |
@jasonbahl Thanks for your feedback on the support for multiple of the same query. I've added your comments to the dedicated issue here: |
Since this feature has been shipped, I'm going to close this issue. Thanks everyone for your feedback! If you have further ideas regarding multiple queries in Faust, please open a separate issue. |
Motivation
Users may want separate queries in their Faust templates instead of one large query to render a template. For example, a Faust template may have a query for the specific post it is fetching (e.g.
query GetPost
), but also have a query that fetches layout related items like menus and general settings (e.g.query GetLayout
). Currently in Faust, you must merge these two queries into one and include it in yourComponent.query
. However, this could be a disadvantage from a caching perspective as you may be re-requesting data that isn't necessarily stale. Additionally, combining everything into a single query is not the best developer experience.API Changes
We will add an additional property to a Faust template called
Component.queries
, that can accept an array of queries, for example:The existing
Component.query
functionality will be preserved. A Faust template could either useComponent.queries
to fetch multiple queries orComponent.query
to fetch a single query, but not both.This data could then be retrieved in the
Component
using theuseFaustQuery
hook:Drawbacks
One potential drawback of this design is that allComponent.queries
will share the same variables, meaning if two queries use different variables, all necessary variables need to be returned from theComponent.variables
property.Queries with their own variables were added to the RFC and are now supported.
How to Contribute
We are Interested in hearing your thoughts on this and any possible enhancement's that you want to see materialized. Do these API changes and conventions make sense?
The text was updated successfully, but these errors were encountered: