how do I shape my graphql backend response? - graphql

I am querying my graphql backend and need the response to have a flatten shape,
my query:
gql`
{
questions {
edges {
id
title
author: user {
email
}
}
}
}
`
my response:
'5d3eafb7889a135ff8cd950c': {
id: '5d3eafb7889a135ff8cd950c',
title: 's',
author: {
email: 'dggdfgdgfd#gmail.com',
__typename: 'User'
},
__typename: 'Question'
},
problem is with author, I need as a string instead of an object:
id: '5d3eafb7889a135ff8cd950c',
title: 's',
author: 'dggdfgdgfd#gmail.com' // <===
`

You have two options:
1) After getting the data from the GraphQL server, transform the data as you need to fit your needs. A Javascript implementation might be:
function flattenGqlResponse (response) {
return Object.keys(response).map((question) => {
return {
id: response[question].id,
title: response[question].title,
author: response[question].author.email
}
}
);
}
2) Flatten the structure in your GraphQL Resolver on your GraphQL Server. Likely, you do not want to pursue this route based on what I see about your schema, unless your User object only has one field, that is email

Related

How to read GrqphQL resolver

While I try to learn GraphQL-Tools.I found this article.In this article,example resolvers are described as follows.
const resolvers = {
Query: {
posts: () => posts,
author: (_, { id }) => find(authors, { id })
},
Mutation: {
upvotePost: (_, { postId }) => {
const post = find(posts, { id: postId })
if (!post) {
throw new Error(`Couldn't find post with id ${postId}`)
}
post.votes += 1
return post
}
},
Author: {
posts: author => filter(posts, { authorId: author.id })
},
Post: {
author: post => find(authors, { id: post.authorId })
}
}
I understood that Queryand MutationandSubscriptionis main function of Graphql resolvers.
My question is what is Author,Post in this resolver ?
I can understand QueryandMutationsection of this resolver. but how to think about Authorand Post?
It seems that it defined functions.
Thanks
That is Trival Resolver
https://graphql.org/learn/execution/#trivial-resolvers
Until Graphql has a completely understandable return value.
Graphql goes on to the next search with the returned type.
Those functions [Author, Post] tell to Graphql how to find the object's value.
For exameple if "PostFindById" reoslver promised to graphql that will return "author" field But instead it return "Post" Graphql will excute Trival Rsolver to find value of author field with given "Post".
So it will execute this resolver with given 'post'
Post: {
author: post => find(authors, { id: post.authorId })
}

Apollo-client: Add item to array in cache

Suppose I have the following GraphQL types:
type User {
id: String!
posts: [Post!]!
}
type Post {
id: String!
text: String,
}
And here is a mutation that returns the updated post:
mutation addNewPost(
$userId: String!
$text: String!
) {
addNewPost(userId: $userId, text: $text) {
id
text
}
}
After running this mutation my cache contains a new entry of a post. How do I add it to the user's posts array? I have tried cache.writeQuery and cache.modify but I cannot figure it out.
We do push the item into array inside the update function, which is one of the options of useMutation.
I'm writing the whole mutation so that you can get the idea 💡, let have a look at example:
By Update function:
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
update(cache, response) {
// Here we write the update
// Step 1: Read/Fetch the data 👈
const data = client.readQuery({
query: FETCH_POSTS_QUERY,
});
// Step 2: Update the cache by immutable way 👈
client.writeQuery({
query: FETCH_POSTS_QUERY,
data: {
getPosts: [response.data.createPost, ...data.getPosts],
},
});
},
variables: formValues,
});
By refetchQueries:
That's really shortcut 🔥 way to update the cache, by using DocumentNode object parsed with the gql function
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
refetchQueries: [ 👈
FETCH_POSTS_QUERY
'fetchPosts`' // OR Query name
],
variables: formValues,
});
You're going to want to directly write to the Apollo cache in order to update the other entities that your mutation has modified.
Have a look at the docs https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates here for specifics (you're going to want to use cache.modify and cache.writeFragment)

Prisma and ApolloClient: Prevent overwriting the include conditions at the backend by the frontend for relations

I have a problem, thx for any help.
With prisma we can use include with where conditions for models with a relation. If I make include conditions I get the right result. If I return it to the frontend it gets overwritten. I want to return exact my result from the backend.
I have at the frontend a query (ApolloClient, gql) like. It will return an array of comments for each post, I just want to have the first Comment for each post.
const POSTS = gql`
query posts {
posts(postId: $postId) {
id
comments{ // at the backend I have conditions for the comments
id
}
}
}
`;
Backend: Primsa and graphql nexus
Prisma Schema
model Post {
id String #id #default(cuid())
comments Comment[]
}
model Comment {
id String #id #default(cuid())
post Post #relation(fields: [postId], references: [id])
postId String
}
Nexus Model
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments()
})
const Comment = objectType({
name: 'Comment',
definition(t) {
t.model.id()
t.model.post()
t.model.postId()
})
Resolver
export const posts = queryField('posts', {
type: 'Post',
list: true,
args: {
...
},
resolve: async (_parent, args: any, { prisma, request }, info) => {
const posts = await prisma.post.findMany({
include: {
comments: {
take: 1
}
}
})
console.log(posts)
//Perfect result I want to return the include condition. But at the frontend I have all
//comments
return posts
},
})
The console.log(posts) is exact what I want to return!. Every post has an Array of ONE Comment.
I return the posts and at the frontend every post has an Array of ALL Comments, what I don't want. How can I prevent that the frontend query overwrite the backend return? The fields are the same.
I can't add a comment, so I am adding this to another answer.
Like I said with my PrismaSelect plugin, you can't use nexus-plugin-prisma t.model, t.crud. You will need to use Pal.Js CLI to autoGenerate all CRUD and ObjectTypes for all models.
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments() // this field will overwritten by next one so this not needed
t.list.field('comments', {
type: 'Comment',
list: true,
resolve: (parent, args, { prisma }) => {
// here parent type include all other fields but not this field
return prisma.comment.findMany({ // this query is very wrong will case N+1 issue
where: {
postId: parent.id,
},
take: 1,
})
},
})
})
Example
model User {
id Int #default(autoincrement()) #id
createdAt DateTime #default(now())
email String #unique
name String?
password String
posts Post[]
comments Comment[]
}
model Post {
id Int #default(autoincrement()) #id
published Boolean #default(false)
title String
author User? #relation(fields: [authorId], references: [id])
authorId Int?
comments Comment[]
}
model Comment {
id Int #default(autoincrement()) #id
contain String
post Post #relation(fields: [postId], references: [id])
postId Int
author User? #relation(fields: [authorId], references: [id])
authorId Int?
}
Here is my Pal.Js CLI generated type for Post model
import { objectType } from '#nexus/schema'
export const Post = objectType({
name: 'Post',
definition(t) {
t.int('id', { nullable: false })
t.boolean('published', { nullable: false })
t.string('title', { nullable: false })
t.field('author', {
nullable: true,
type: 'User',
resolve(parent: any) {
return parent['author']
},
})
t.int('authorId', { nullable: true })
t.field('comments', {
nullable: false,
list: [true],
type: 'Comment',
args: {
where: 'CommentWhereInput',
orderBy: 'CommentOrderByInput',
cursor: 'CommentWhereUniqueInput',
take: 'Int',
skip: 'Int',
distinct: 'CommentDistinctFieldEnum',
},
resolve(parent: any) {
return parent['comments']
},
})
},
})
when you use my Pal.js CLI, your frontend request will be like this
query {
findOnePost(where: {id: 1}) {
comments(where: {}, take: 1){
id
}
}
}
``
The best way to handle this issue and just query what your frontend request to use my PrismaSelect plugin.
Prisma Select takes the info: GraphQLResolveInfo object in general graphql arguments (parent, args, context, info) to select object accepted by prisma client. The approach allows a better performance since you will only be using one resolver to retrieve all your request. By doing so, it also eliminates the N + 1 issue.
Also, you can use my CLI to autogenerate all CRUD from your schema.prisma file https://paljs.com/generator/nexus
I mean I can add to my Post-ObjectType a field condition like:
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.comments()
t.list.field('comments', {
type: 'Comment',
list: true,
resolve: (parent, args, { prisma }) => {
return prisma.comment.findMany({
where: {
postId: parent.id,
},
take: 1,
})
},
})
})
This is working. But if I understood it correct I have for every post one extra request. But I have already at the mutation resolver the right result. And I don't have the comments field at the parent (t.list.field- resolver)

Each Node of data perform separate database request

below is the GraphQLObject Fields
userId: {
type: GraphQLID,
resolve: obj => {
console.log(obj._id);
return obj._id;
}
},
email: { type: GraphQLString },
password: { type: GraphQLString },
firstName: { type: GraphQLString },
lastName: { type: GraphQLString },
mine server sents multiple request equally as of mine documents, here it will send 5 different request.
how can i optimize these request get all data in one request
589800cf39b58b29c4de90dd
--------------------------------
58980673e7c9a733009091d1
--------------------------------
58985339651c4a266848be42
--------------------------------
589aac5f884b062b979389bc
--------------------------------
589aad9d24989c2d50f2a25a
In such a case you could create a query method which would accept an array as a parameter, which would be an array of IDs in this case.
getUsers: {
type: new GraphQLList(User),
args: {
ids: {
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID)))
}
},
resolve: (root, args, context) => {
let query = 'SELECT * FROM users WHERE id = ANY($1)';
return pgClient.query(query, [args.ids], (err, result) => {
// here you would access all returned rows in the result object
console.log(result);
});
}
}
The query variable would differ depending on what database you are using. In this example I have used the node-postgres module for PostgreSQL. However, the concept is the same - use array of ids to perform single query returning all users.
And then you could call that query:
query getUsers($ids: [ID!]!) {
getUsers(ids: $ids){
id
email
...
}
}
// and the variables below
{
ids: ['id#1', 'id#2', 'id#3']
}
This is a job for Dataloader, a library from Facebook specifically for batching together queries like this:
https://github.com/facebook/dataloader

Nested query for the new GraphQL buildSchema

How can I make resolver for my friendList with the new GraphQL Schema language? friendList have an array of people _id.
My new people type with GraphQL Schema language:
const schema = buildSchema(`
type People {
_id: String
firstName: String
lastName: String
demo: String
friendList: [People]
}
type Query {
getPeople(_id: String): People
}
`);
My old people type with GraphQLObjectType:
const PeopleType = new GraphQLObjectType({
name: 'People',
fields: () => ({
_id: {
type: GraphQLString,
},
firstName: {
type: GraphQLString,
},
lastName: {
type: GraphQLString,
},
friendList: {
type: new GraphQLList(PeopleType),
// pass #friends parentValue
resolve: ({ friends }) {
return People.find({ _id: { $in: friends } }).then(res => res);
},
}),
});
I want to achieve this query:
{
people(_id: "ABC123") {
firstName
lastName
friendList {
firstName
lastName
}
}
Your resolver should return a new instance of a class as explained in the updated GraphQL documentation: http://graphql.org/graphql-js/object-types/.
class People {
friendList () {}
}
var rootValue = {
getPeople: function () {
return new People();
}
}
I couldn't find a way to do this with graphql-js so I switched to a similar implementation from the folks at Apollo called graphql-tools.
http://dev.apollodata.com/tools/graphql-tools/generate-schema.html
They have a function called makeExecutableSchema that takes a schema written in the GraphQL Schema language and combines it with (nested) resolver functions. This fixes the issue and is actually pretty simple to integrate with if your code uses the GraphQL Schema language.
They also have another agnostic tool for exposing this new schema that replaces express-graphql, its GraphQL-server:
http://dev.apollodata.com/tools/graphql-server/index.html
Check out the docs and hopefully this gives you the flexibility to write complex code using the GraphQL Schema language syntax!

Resources