-
-
Notifications
You must be signed in to change notification settings - Fork 101
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
Providing different variables for pagination instead of offset and limit #445
Comments
As a temporary solution, I extend schema and created a new query with different variable names. |
Unfortunately, there isn't a way to do this yet. I'm been wrestling with what the API could look like for providing custom pagination implementations. Do you have any ideas? |
This is the case with FaunaDB as well: type Query {
allTemplates( _size: Int _cursor: String ): TemplatePage!
}
type TemplatePage {
data:[Template]!
after: String
before: String
} |
@AlecAivazis I'm open to do a PR if you agree. /** @type {import('houdini').ConfigFile} */
const config = {
//...
pagination: {
fields:{
limit:{
arg: 'take',
type: 'Int'
},
offset:{
arg: 'skip',
type: 'Int'
}
}
}
} |
Today houdini supports 2 styles in a 'hard coded way'. One option (A) is to support 3 or 4 ways in a 'hard coded way', another option (B) is to give users the ability to provide custom fields and logic. Another option (C) is to map style 3 to style 2 (what I think you suggest with the config update). The best would be B as it gives the most freedom and houdini can provide n styles directly included. Could you imagine starting a PR for A? Or what do you think? |
I'm still reading the source. Since I want to be able to use Houdini's pagination features with fauna for work, I'm open to make a PR with my fork maybe later today, but the thing is I see this as a more of an easy fix. I'm in favour of C as well, I'll see what I can do. |
What about having fields on the |
I just realized I never got down my full thoughts on this. I think if our goal is to support custom approaches to pagination it would be helpful to figure out what are the various parts that would have to be customizable in order to support the two current options. That is to say, that the current pagination logic should exist as a built in version of whatever a user would do to specify their own. So with that in mind here is what we need to let the user specify:
I know this is probably a lot more than people were expecting but I think it would be good to get on the same page about what's needed in order to support custom pagination strategies. I think a field mapping like @DanielHritcu is a good idea but only works in the absolute simplest cases that are directly equivalent to what's already supported. This feature is 1000% necessary but very very hard to get right which is why I haven't really spent too much time on it. This need for custom methods on the stores makes me think that we should let users be able to provide custom classes that we will use when instantiating a paginated store. That means that there are 2 parts of the API that we need to consider: the interfaces of classes themselves as well as the config value pointing to the custom class. |
Since the current Would a middleware approach work better? My rough and maybe wrong idea would be to alter the document AST before passing it to the paginate or list directive and change it back when the client is built. Don't know where along Houdini's transformer process the two functions will be inserted. For more custom use cases, that go outside the already existing strategies ( cursor, offset), maybe provide a custom transformer to replace the existing one ? Config could be something like: // ...
/** @type {import('houdini').ConfigFile} */
const config = {
client: './src/lib/houdini.ts',
schema: './schema.graphql',
transformers: {
pagination: customPagination
}
}; In case the already existing strategies can provide the functionality: // names are chosen for the sake of the example.
export const paginationMiddleware = {
alter: (ast: DocumentNode) => {
return visit(ast, {
// We look for `_size` and `_cursor` arguments and change them to
// `first`, `after` to fit Houdini's implementation.
Argument(node) {
switch (node.name.value) {
case '_size':
(node as any).name.value = 'first'
return node
case '_cursor':
(node as any).name.value = 'after'
return node
default:
break
}
},
// ...
})
},
revert: (ast: DocumentNode) => {
return visit(ast, {
Argument(node) {
// Revert the argument names back.
switch (node.name.value) {
case 'first':
(node as any).name.value = '_size'
return node
case 'after':
(node as any).name.value = '_cursor'
return node
default:
break
}
},
// ...
})
},
// Don't exactly know if the response needs to be remapped also.
// Also if this would affect the generated types.
} Config could be something like: import {paginationMiddleware} from '@houdini/middleware/pagination-fauna'
/** @type {import('houdini').ConfigFile} */
const config = {
client: './src/lib/houdini.ts',
schema: './schema.graphql',
middleware: {
pagination: paginationMiddleware
}
};
export default config; At the moment I used something like this on my graphql yoga server to remap requests and responses from But this requires that I maintain a schema Houdini is compatible with, and also I need to perform the remapping on each request. If I could perform this transformation at build time with Houdini, that would be amazing. |
Sorry I haven't replied here yet @DanielHritcu. Every time I read what you wrote, I get distracted with some thought process and end up writing a bunch of notes for what a Houdini plugin should look like. I think you're onto something big 👍 I've been working on fleshing this idea out a little bit for the last few days to figure out the best way to support multiple frameworks and i think what you described is falling very nicely into how I've been thinking about it. Mind if I ping you for feedback when I have something a little more concrete? |
Okay so with If someone is interested in helping connect the remaining dots, I would gladly provide any assistance they required. probably good start with a voice chat to go over what's in place, what's still needed, etc. |
@AlecAivazis |
No problem at all! Discord is best - you can find me on the svelte discord as @AlecAivazis |
Just to add another example to the mix, Postgraphile uses |
Oh that's really good to keep in mind! We do try to be a little smarter than that and inspect the schema to see which strategy it satisfies (so for cursors, it looks for |
@AlecAivazis Sorry if I'm misunderstanding the last part of what you said, but are you implying there is a way currently to use Automatic Loading with the @paginate directive that supports using first/offset instead of limit/offset? The only way I've been able to do this so far is to remove @paginate entirely and use either 1) Manual Loading or 2) by using the _XVariables method in +page.js to pull out a Is there a better way to do this now that I'm just not gleaming from the docs? For clarification, I'm doing this:
This definitely seems like overkill, so if you have any other suggestions for how to do this now that are less verbose, I'm all ears. |
No, not yet. There's no way yet to customize the pagination arguments. I just wanted to clarify that we wouldn't have to change the top level api of assigning |
For your exact situation @lusid, I would probably move the query definition into the // +page.js
import { graphql } from '$houdini'
export const _houdini_load = graphql(`
query Info($first: Int = 10, $offset: Int = 0) {
tests(first: $first, offset: $offset) {
nodes {
id
name
}
pageInfo {
hasPreviousPage
hasNextPage
}
}
}
`)
export function _InfoVariables({ url }) {
const p = parseInt(url?.searchParams?.get('p')) || 1
return {
first: 10,
offset: (p - 1) * 10
}
} Also, it might be helpful to know that the store value has an attribute that holds the variables so you don't need to do |
A very rough note without any context for future alec that i don't want to forget: we could allow users to add custom updates to the artifact that they can teach the cache how to perform. Atm, we support prepend and append for our pagination logic. maybe the user wants to define a special patch update that let's them do something crazy when applying a server response to the cached value. this could be used for custom pagination implementations or even to make situations look like pagination that don't conform to houdini's assumptions. |
Any updates on this one? I'm trying to test with GraphQL Zero and am not sure how to set up the paging without doing everything manually. |
I'm using KeystoneJS graphql as my backend and they schema look like this:
KeystoneJS using take and skip instead of offset and limit. Is there a way to support those variable names?
The text was updated successfully, but these errors were encountered: