Must included all data attribute inside reference as the typeDefs value. It's okies, if the value is null.
In apollo-client: 2.6.10
TypeDefs Graphql and Mutation below:
type Product {
_id: ID
name: String!
benefit: String
country: String
vitamins: [String]
createdAt: String!
}
const [addFruit] = useMutation(ADD_FRUIT_MUTATION, {
update(cache, result) {
const cachedData = cache.readQuery({
query: GET_FRUTIS_QUERY,
});
cachedData.getFruits = [
{
...result.data.create_fruit, // name, vitamins, country, benefit
createdAt: new Date().toISOString(), // added this
_id: result.data.create_fruit.name, // added this, this only temporary and must unique.
},
...cachedData.getFruits,
];
cache.writeQuery({
query: GET_FRUTIS_QUERY,
data: { ...cachedData },
});
},
onError({ networkError, graphQLErrors }) {},
variables: fruitVariable,
});
hope this helps someone. =)
I wondering why it must all included the data attribute key? if dont, it will updated cached but not into UI.
Related
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)
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)
This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 2 years ago.
I'm having some trouble with a graphQL query returning null and i'm not quite sure why its returning null. I've read several posts similar to this post but none of those posts have helped identify my exact problem.
I think it has something to do with how i'm passing/getting parameters, since my query with no parameters works fine, but i'm not sure since I can't find any other examples online.
Any help would be greatly appreciated.
I'm using apollo-server, graphql, and a community SQL datasource implementation which uses Knex to create a database connection.
There are two queries I can make.
allParts() - this query works as expected and returns all parts in my database with requested graphQL fields
getPart(itemnum) - this is the query that is not currently working.
graphQL Query:
query{
getPart(itemnum: "T|0000000000001"){
desc
}
}
graphQL response:
"message": "Cannot return null for non-nullable field Part.desc.",
SQL query that is being executed based on Knex debug message:
method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'T|0000000000001' ],
__knexQueryUid: '3a8234eb-0a5c-46db-ad8e-5508288c9a86',
sql: 'select * from `part` where `itemnum` = ?'
index.js:
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require ('./resolvers')
const PartAPI = require ('./datasources/partAPI');
const knexConfig = {
client: "sqlite3",
connection: {
/* CONNECTION INFO */
filename: "./TEAM_material.db3"
},
debug: true
};
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => ({
partAPI: new PartAPI(knexConfig),
}),
introspection: true,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
partAPI.js:
const { SQLDataSource } = require("datasource-sql");
const MINUTE = 60;
class PartAPI extends SQLDataSource {
getPart(itemnum){
return this.knex.select('*').from('part').where({itemnum});
}
getAllParts(){
const query = this.knex.select('*').from('part').cache(MINUTE);
console.log (query);
return query;
}
}
module.exports = PartAPI;
schema.js
// src/schema.js
const { gql } = require('apollo-server')
const typeDefs = gql`
#Types
type Part {
itemnum: String!
desc: String!
vendor: String!
manuf: String!
venlist: Float!
price: Float!
teamlist: Float!
teamsell: Float!
unitypart: String!
pkgqty: Int!
ioType: String!
preferred: Boolean!
filterlvl1: String!
filterlvl2: String!
filterlvl3: String!
filterlvl4: String!
ipwiretype: String!
opwiretype: String!
obsolete: Boolean!
}
#Queries
type Query {
getPart(itemnum: String!): Part
allParts: [Part!]!
}
`;
module.exports = typeDefs
resolvers.js
// src/resolvers.js
const resolvers = {
Query: {
getPart: (_,args,{dataSources}) => dataSources.partAPI.getPart(args.itemnum),
allParts: (_,__,{dataSources}) => dataSources.partAPI.getAllParts(),
},
};
module.exports = resolvers
turns out my schema was incorrect. The getPart query was expecting a Part but instead my query was returning an array of Part.
old Schema
#Queries
type Query {
getPart(itemnum: String!): Part
allParts: [Part!]!
}
`;
new Schema
#Queries
type Query {
getPart(itemnum: String!): [Part]!
allParts: [Part!]!
}
`;
I'm setting up a react-admin app, that needs to connect with a Hasura Service using a graphql provider. To do so, I need to pass for the provider the endpoint "/v1/graphql" and the query with the selects subfields. Like this:
query MyQuery {
account_customers {
customer_id
email
given_name
}
}
I tried to use the Hasura Provider (https://github.com/hasura/ra-data-hasura/) but the requisitions are going to "/v1/query", and I couldn't found how to change it. Also couldn't figure out how to send my custom query with the subfields.
I also tried to use ra-data-graphql-simple provider, to override a query
to get the resource with the subfields.
/providers/myProvider.js
import buildGraphQLProvider, { buildQuery } from 'ra-data-graphql-simple';
import gql from 'graphql-tag';
const myBuildQuery = introspection => (fetchType, resource, params) => {
const builtQuery = buildQuery(introspection)(fetchType, resource, params);
if (resource === 'account_customers' && fetchType === 'GET_LIST') {
return {
// Use the default query variables and parseResponse
...builtQuery,
// Override the query
query: gql`
query ($id: ID!) {
account_customers {
customer_id
email
name
}
}`,
};
}
return builtQuery;
}
export default buildGraphQLProvider({ buildQuery: myBuildQuery })
App.js
import buildGraphQLProvider from './providers/myProvider.js';
class App extends Component {
constructor() {
super();
this.state = { dataProvider: null };
}
componentDidMount() {
buildGraphQLProvider({ clientOptions: { uri: 'http://localhost:8080/v1/graphql' }})
.then(dataProvider => this.setState({ dataProvider }));
}
render() {
const { dataProvider } = this.state;
return (
<div className="App">
<Admin dataProvider={dataProvider} >
<Resource name="account_customers" list={ListGuesser} />
</Admin>
</div>
);
}
}
export default App;
But I'm getting the error:
"Unknown resource account_customers. Make sure it has been declared on your server side schema. Known resources are "
I have had a similar issue.
The react-admin data provider you're using expects a certain shape of the GraphQL API, so it can get all items, create an item, update an item etc.
I have solved it by making sure that my schema complies with this:
type Query {
Post(id: ID!): Post
allPosts(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): [Post]
_allPostsMeta(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): ListMetadata
}
type Mutation {
createPost(
title: String!
views: Int!
user_id: ID!
): Post
updatePost(
id: ID!
title: String!
views: Int!
user_id: ID!
): Post
deletePost(id: ID!): Post
}
type Post {
id: ID!
title: String!
views: Int!
user_id: ID!
User: User
Comments: [Comment]
}
input PostFilter {
q: String
id: ID
title: String
views: Int
views_lt: Int
views_lte: Int
views_gt: Int
views_gte: Int
user_id: ID
}
type ListMetadata {
count: Int!
}
So in your case, you would need these endpoints (and change the naming in your backend):
AccountCustomer
allAccountCustomers
_allAccountCustomersMeta
updateAccountCustomer
createAccountCustomer
deleteAccountCustomer
etc...
I have defined Author as below:
const Author = new GraphQLObjectType({
name: 'Author',
description: 'Represent the type of an author of a blog post or a comment',
fields: () => ({
_id: {type: GraphQLString},
name: {type: GraphQLString},
posts: {type: Post}
})
});
I have defined my Query as below
const Query = new GraphQLObjectType({
name: "Root_Query",
fields: {
authors: {
type: new GraphQLList(Author),
args: {_id: { type: GraphQLString },name:{type: GraphQLString}},
resolve: function(rootValue, args, info) {
let fields = {};
let fieldASTs = info.fieldASTs;
fieldASTs[0].selectionSet.selections.map(function(selection) {
fields[selection.name.value] = 1;
});
return authorsCollection.find({}, fields).toArray();
}
}
Now when I query for author with particular id by using the query given below
{
authors(_id: "57c5794a92aef65040c4e0e6"){
_id
name
}
}
Instead of displaying author with _id 57c5794a92aef65040c4e0e6 .It is displaying all the authors _id and name. How can I solve the problem??
The problem is with the following DB code, which fetches all authors because of empty query {}:
return authorsCollection.find({}, fields).toArray()
Add _id to your query:
// import {ObjectID} from 'mongodb';
const authorId = ObjectID.createFromHexString(args._id);
return authorsCollection.find({_id: authorId}, fields).toArray()
You have to