Prisma cursor based pagination - graphql

Just finished the excellent tutorial on building Prisma based graphql backend. They explain how to implement first-offset pagination here https://www.howtographql.com/graphql-js/8-filtering-pagination-and-sorting/.
Now I am wondering, how to implement cursor-based pagination?
Here are my types (they are the same as in tutorial):
type User
implements Node {
id: ID!
name: String!
email: String!
password: String!
links(...): [Link!]
votes(...): [Vote!]
}
type Link
implements Node {
id: ID!
createdAt: DateTime!
description: String!
url: String!
postedBy(...): User
votes(...): [Vote!]
}
In the playground I am trying to query user information along with the links, created by the user:
{
user(where: {id:"cjimzqrshb3nf0c29z1p7km0j"}) {
email
links {
id
url
description
}
}
}
It gives me back all the links, created by the user. How can I paginate them? Links object does not have paging information while linksConnection does not fit inside user object.

You can query the linksConnection related to the user, and therefore access cursors :
{
linksConnection(where:{user:{id:"cjimzqrshb3nf0c29z1p7km0j"}}){
pageInfo{
endCursor
startCursor
}
edges{
cursor
}
}
}

You can implement cursor based pagination with Prisma like this:
{
users{
links(first: 10, after:"some-id") {
description
}
}
}
This is possible because the id and the cursor is the same.
Alternatively you can use offset based pagination like this:
{
users{
links(first: 10, skip: 30) {
description
}
}
}

Related

How to resolve Inconsistent __typename error in Relay?

I just tried to implement the Relay in Frontend for this graphql tutorial, In that tutorial, they created graphql server to store URL(Link) bookmarks with the User who posted those URLs.
The relationship between the link and the users is:
Link belongs_to :user,
User has_many :links.
And I listed out all the Links with Users in Frontend, at the time I got the below error.
Warning: RelayResponseNormalizer: Invalid record 1. Expected __typename to be consistent, but the record was assigned conflicting types Link and User. The GraphQL server likely violated the globally unique id requirement by returning the same id for different objects
I'm not aware of how much it will impact the application. because I got the expected result from Frontend.
Frontend View of Query.
I read this relay official blog for this kind of error, but there is no example to know how exactly to resolve this. so can someone help to resolve this?
Relay Query
graphql`
query LinkListQuery {
allLinks {
id,
description,
url,
postedBy {
id,
name
}
}
}`
Schema:
input AUTH_PROVIDER_CREDENTIALS {
email: String!
password: String!
}
input AuthProviderSignupData {
credentials: AUTH_PROVIDER_CREDENTIALS
}
type Link implements Node {
description: String!
id: ID!
postedBy: User
url: String!
votes: [Vote!]!
}
input LinkFilter {
OR: [LinkFilter!]
descriptionContains: String
urlContains: String
}
type Mutation {
createLink(description: String!, url: String!): Link!
createUser(name: String!, authProvider: AuthProviderSignupData): User!
createVote(linkId: ID): Vote!
signinUser(credentials: AUTH_PROVIDER_CREDENTIALS): SignInUserPayload
}
"""An object with an ID."""
interface Node {
"""ID of the object."""
id: ID!
}
type Query {
allLinks(filter: LinkFilter, first: Int, skip: Int): [Link]!
"""Fetches an object given its ID."""
node(
"""ID of the object."""
id: ID!
): Node
}
"""Autogenerated return type of SignInUser"""
type SignInUserPayload {
token: String
user: User
}
type User implements Node {
email: String!
id: ID!
links: [Link!]!
name: String!
votes: [Vote!]!
}
type Vote {
id: ID!
link: Link!
user: User!
}

How to properly append to an array using Dgraph GraphQL API?

I'm trying to spin up Dgraph, but appears as though to add a node to an array of nodes using the GraphQL api requires an unnecessary amount of work if I understand it correctly:
I have the following simplified schema:
type User #secret(field: "password") {
account: String! #id
email: String! #search(by: [hash])
extension: String! #search(by: [hash])
phone: String! #search(by: [hash])
hasCreated: [Transaction]! #hasInverse(field: from)
hasReceived: [Transaction]! #hasInverse(field: to)
}
type Transaction {
id: ID!
type: TransactionType! #search(by:[terms])
amount: Float!
assetCode: String! #search(by:[terms])
to: User!
from: User!
initiatedAt: DateTime! #search(by:[hash])
completedAt: DateTime #search(by:[hash])
status: Status!
}
To me, it appears as though to add a node to the User's hasCreated or hasReceived fields would require me to pull the entire array, append a new Transaction to the array and then use an updateUser mutation to complete the update. But the updateUser mutation would require me to get all the Transactions and all Users attached to these transactions and so on.
Example trying to retrieve an entire user object:
query {
getUser(id:"%s"){
account
email
extension
phone
hasCreated {
id
type
amount
assetCode
to {
...
}
}
hasReceived {
id
type
amount
assetCode
to {
...
}
}
}
}
}
Is there another way to append to arrays or update objects using the GraphQL api without having to retrieve whole objects?

How to resolve subselections / relations in prisma (nested lists)

Let's take an example from the github repo of prisma:
We have a user, the user could have multiple posts, and one post could have multiple links.
My goal is, to retrieve all posts and all links.
This means, my response is a list (links) in a list (posts).
I want to map the values I get back as two nested lists.
datamodel.prisma
type User {
id: ID! #id
email: String! #unique
name: String
posts: [Post]!
}
type Post {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
published: Boolean! #default(value: false)
title: String!
content: String
author: User!
links: [Link]!
}
type Link {
id: ID! #id
url: String
title: String
post: Post!
}
schema.graphql
type Query {
...
}
type Mutation {
...
}
type Link {
id: ID!
url: String
title: String
post: Post!
}
type Post {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
published: Boolean!
title: String!
content: String
author: User!
}
type User {
id: ID!
email: String!
name: String
posts: [Post]!
}
I want to query all posts of a user, and all of the links for every post in the response.
How would I query this request?
user {
id
posts {
id
links {
id
}
}
}
The above code snipper would not work.
EDIT
I want to use the following:
User: {
listPosts: (parent, args, context, info) {
return context.prisma.posts().links()
}
}
So in my response (data in front-end via react-apollo Query Component), I want to map over posts AND the links in each post.
BUT the links attribute in posts is null.
Is there another way to achieve this?!
According to the docs:
Prisma client has a fluent API to query relations in your database. Meaning you can simply chain your method calls to navigate the relation properties of the returned records. This is only possible when retrieving single records, not for lists. Meaning you can not query relation fields of records that are returned in a list.
In order to get around that limitation, you can use the $fragment method:
const fragment = `
fragment UserWithPostsAndLinks on User {
id
email
name
posts {
id
title
content
links {
id
url
title
}
}
}
`
const userWithPostsAndLinks = await prisma.user({ id: args.id }).$fragment(fragment)

Prisma append custom field to info argument when querying data

I am trying to append extra fields to the info object when querying data from prisma database. I have seen this post but I can't get fragments working.
I have below migrations:
type User {
id: ID! #unique
name: String
}
type Video {
id: ID! #unique
name: String
likes: [Like]
}
type Like {
id: ID! #unique
user: User
createdAt: DateTime!
}
Now, I would like to query all videos and check if current user has already liked a video. If so, the likes object should read the createdAt value, otherwise it would be empty.
I am doing this in playground via following query:
query {
videos() {
id
name
hasVoted: likes(where: {user: {id: "cjr7r85jy00rc0892dfwpu96u"}){
createdAt
}
}
}
this works but I would like to add the hasVoted to my query resolver to automatically append it to all queries. How can I do this ?
I tried solving it via fragments but without luck:
videos: {
fragment: `fragment hasVoted on Video {
hasVoted: likes (where: {user: {id: "cjr7r85jy00rc0892dfwpu96u"}}){
createdAt
}
}`,
resolve: async (_, args, ctx, info) => {
return await ctx.prisma.query.videos({},info);
}
}
Anyone has some ideas how I can do this? thx!
You can use addFragmentToInfo from graphql-bindings
https://oss.prisma.io/content/graphql-binding/02-api-reference#addfragmenttoinfo

Indexing List Type field in a GraphQL type from within a Query

Say I have the following GraphQL Schema
query {
allAuthors: [Author]
}
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
name: String!
author: Author!
}
Now I can successfully run the following query to get all the authors and their associated books
query {
allAuthors {
name,
books {
name
}
}
}
However, if I only want to get the first three books for all authors, how would I go about doing something like that? Can we index the books field in the Author type from within a query? If so, how?
I tried something like this and it doesn't work
query {
allAuthors {
name,
books[3] {
name
}
}
}
GraphQL doesn't have syntax for this.
You can add a "limit" parameter to a field, and this is common enough:
type Query {
allAuthors(limit: Int, offset: Int): [Author!]!
}
type Author {
id: ID!
name: String!
books(limit: Int, offset: Int): [Book!]!
}
If you add parameters like this to the schema, then the query you want (for all authors, get the first three books) could look like
{
allAuthors {
name
books(limit: 3) {
name
}
}
}

Resources