GraphQL query error -- variable is never used in operation - graphql

I am performing a request for an individual post from Apollo Server / Express backend.
In the Apollo GraphQL sandbox, the query works and retrieves the correct post, however, the query has a red squiggle identifying an error which reads -
Variable "$getPostId" is never used in operation "Query".
The query is as follows -
query Query($getPostId: ID!) {
getPost(id:"20c9b3ac-afe6-4faa-a3f9-e00ef1b38ccf") {
title
author
id
}
}
The schema is as follows -
module.exports = gql`
type Post {
id: ID!
title: String!
author: String!
}
type Query {
getPosts: [Post]!
getPost(id: ID!): Post
}
...
`
The closest post which seems to address a similar problem I could find is here. However, I can't translate the resolution to my problem.
Why is the error showing (particularly when the query runs successfully)? What needs to be done to stop the error from showing?
Many thanks!

It sounds like
query Query($getPostId: ID!) {
getPost(id:"20c9b3ac-afe6-4faa-a3f9-e00ef1b38ccf") {
title
author
id
}
}
is supposed to be
query Query($getPostId: ID!) {
getPost(id: $getPostId) {
title
author
id
}
}
Or if your query is actually meant to hard-code the ID, then you want
query Query {
getPost(id:"20c9b3ac-afe6-4faa-a3f9-e00ef1b38ccf") {
title
author
id
}
}

Related

Mutation not updating Graphcache cache entry in urql

I am working on a skin care website, and it lets you create skin care routines (Routine in type-defs) with information about how you use your skin care products (ProductUsages in type-defs).
Routine and ProductUsages are many-to-many relations. In type-defs,
type Routine {
id: ID!
# ...
productUsages: [ProductUsage!]
}
type ProductUsage {
id: ID!
# ...
routines: [Routine]
}
On the routine page, urql runs the currentRoutine query like this:
const ROUTINE_QUERY = gql`
query CurrentRoutineQuery($routineId: ID!, $ownerId: ID!) {
currentRoutine(ownerId: $ownerId) {
productUsages {
id
productId
name
brand
category {
id
label
}
frequency
rating
}
id
}
productsWithOtherUsers(routineId: $routineId)
}
`;
(only currentRoutine query is relevant but including everything here just in case)
As you can see, even though it queries a Routine, I'm more interested in ProductUsages in that routine.
Its type-def is as follows:
currentRoutine(ownerId: ID!): Routine
On the same page, users can search and submit new ProductUsages, with the following type-defs.
createProductUsageInfo(input: CreateProductUsageInfoInput): ProductUsage
I run this mutation like
const CREATE_PRODUCT_INFO_MUTATION = gql`
mutation createProductUsageInfo($input: CreateProductUsageInfoInput) {
createProductUsageInfo(input: $input) {
id
weeklyFrequency
dailyFrequency
rating
comment
routines {
id
}
}
}
`;
In the resolver, I create and return a productUsage, and include the related routines entity. Graphcache uses id as the key, so I made sure to query id for the productUsage, and for the included routines.
However, the productUsages in currentRoutine query cache, which I mentioned in the beginning, doesn't reflect the new ProductUsage entry created from this mutation. On the urql cache explorer, productUsages doesn't change.
What could I be doing wrong? I've spent so much time over the last few weeks trying to debug this.
The only thing that I can think of is that the productUsages in currentRoutines result returned from the resolver looks like productUsages: [{productUsage: {id: 1, ...}}, {productUsage: {id: 2, ...}}], so I included the following resolver under Routine to transform it like productUsages: [{id: 1, ...}, {id: 2, ...}].
async productUsages(parent) {
return parent.productUsages.map(
(productUsage) => productUsage.productUsage
);
}
Maybe it doesn't recognize the id because of this? I'm really not sure how to fix this.
It looks like you're missing __typename in the response. This is needed for URQL to match the ID to a key (Product:123). In addition to id, be sure to specify __typename. If you're using graphql-codegen, it has an option to do this automatically.
Your updated query would be:
const ROUTINE_QUERY = gql`
query CurrentRoutineQuery($routineId: ID!, $ownerId: ID!) {
currentRoutine(ownerId: $ownerId) {
productUsages {
id
productId
name
brand
category {
id
label
__typename
}
frequency
rating
__typename
}
id
__typename
}
__typename
productsWithOtherUsers(routineId: $routineId)
}
`;
Then, do the same for your mutation:
const CREATE_PRODUCT_INFO_MUTATION = gql`
mutation createProductUsageInfo($input: CreateProductUsageInfoInput) {
createProductUsageInfo(input: $input) {
id
weeklyFrequency
dailyFrequency
rating
comment
__typename
routines {
id
__typename
}
}
}
`;
Remember, only the fields you fetch in your mutation will update the cache. So in this case, your routines isn't updating any fields, since you're just fetching a list of them. But if that's intended, then that's fine.

Most efficient way to make a database query on GraphQL

I have the following type definition for the type Post
type Post {
id: ID!
title: String!
body: String!
user:User!
indexRef: Int!
createdAt: String!
daysFromCreation: Int!
comments: [Comment]!
}
I'd like to get the fields of the types that are asked on the query, and the do the select with the asked fields. I already got this by using the graphql-fields package. So the resolver ended up like this
const posts = async (parent, args, {dataSources}, info) => {
const askedFields = Object.keys(graphqlFields(info))
const posts = await dataSources.postsApi.getPosts(askedFields.join(','))
return posts
}
Doing the following query
query getPosts{
posts{
title,
body
}
}
}
It'll end up doing a select title, posts from posts_table, but the problem is if I ask for the comment field for example, that's a "complex field" which has its own field resolver. If I ask for the comment field
query getPosts{
posts{
title,
body,
comments {
comment
}
}
}
}
The SQL command will be select title,body,comments from posts_table and that'll throw an exception, because comments of posts are in another table. So, any suggestion about how could I improve the database querying part? I don't like the idea of doing select *.
I thought about checking if the field that's about to be included in the query, is part of the type that the resolver returns... If it's, then include it in the query, if not, let it pass because it'll be resolved in the correspondent field resolver. Is it possible?

Query Appsync graphql with 3 different combinations

I am using appsync with amplify and trying to figure out how to query based on two different selectors. Basically I need to either query all if neither county or facility are supplied, query with county while facility is empty, or query with facility while county is empty. I thought I could wrap this into 1 query but it doesn't seem like I can. My appsync schemas look like this.
type Client
#model
#key(name: "clientByCountyOrFacility", fields: ["county", "facility"], queryField: "getClientsByCountyOrFacility")
#searchable {
id: ID!
facility: String!
county: String!
products: [Product] #connection(name: "ClientProducts")
}
type Product
#model
#searchable {
id: ID!
client: Client #connection(name: "ClientProducts")
}
I can get this to work by using (below query) but I am worried this will run into the 100 scan limit because it uses the listClients query underneath. Possibly if there was an easy way to change that could be a solution but it seems the files in amplify are autogenerated.
query getClientsByCountyOrFacility($county: String = "", $facility: String = "") {
listClients(filter: {
county: {
contains: $county
}
facility: {
contains: $facility
}
}) {
items {
id
products {
items {
id
}
}
}
}
}
I added the #key to see if I could create an index but it doesn't like that and I'm at a lose for how to acquire the data. How do I go about building this schema and query to get the data back?

How should I query and match data from the same response in GraphQL with Apollo Client and Link Rest?

I have the following query:
const getPage = gql`
query Page($path: String!) {
page(path: $path) #rest(type: "Page", path: "{args.path}") {
blocks #type(name: Block) {
name
posts #type(name: Post) {
body
author
}
}
authors #type(name: Author) {
name
}
}
}
In blocks.posts.author there's only an AuthorId. The authors object is containing all the available authors.
I'd like to replace/match the AuthorId with it's corresponding object. Is it possible to do this within one query?
I also wouldn't mind to have a separate query for Author only (fetch will be cached, no new request would be made), but I still don't know how would I match it through 2 queries.
Example API response
{
blocks: [
{
posts: [
{
id: 1,
title: 'My post',
author: 12,
}
]
}
],
authors: [
{
id: 12,
name: 'John Doe'
}
]
}
What I want with 1 query that author inside a post becomes the full author object.
Great question. With GraphQL, you have the power to expand any field and select the exact subfields you want from it, so if you were using GraphQL on your backend as well this would be a non-issue. There are some workarounds you can do here:
If all of the Author objects are in your Apollo cache and you have access to each Author's id, you could use ApolloClient.readFragment to access other properties, like this:
const authorId = ...; // the id of the author
const authorInfo = client.readFragment({
id: authorId,
fragment: gql`
fragment AuthorInfo on Author {
id
name
# anything else you want here
}
`,
});
Although it's worth noting that with your original query in the question, if you have all of the Author objects as a property of the query, you could just use Javascript operations to go from Author id to object.
const authorId = ...; // the id of the author
data.page.authors.find(author => author.id === authorId);
The following should work.
First, capture the author id as a variable using the #export directive. Then add a new field with some name other than author and decorate it with the #rest, using the exported variable inside the path.
So the query would look something like this:
query Page($path: String!) {
page(path: $path) #rest(type: "Page", path: "{args.path}") {
blocks #type(name: Block) {
name
posts #type(name: Post) {
body
author #export(as: "authorId")
authorFull #rest(
path: '/authors/{exportVariables.authorId}'
type: 'Author'
) {
name
}
}
}
authors #type(name: Author) {
name
}
}
}
You can use the fieldNameNormalizer option to rename the author property in the response to a field with a different name (for example, authorId). Ideally, that should still work with the above so you can avoid having a weird field name like authorFull but apollo-link-rest is a bit wonky so no promises.

GraphQL non nested relation

I'm trying to have a representation of nodes on GraphQL more akin to what jsonapi would be like http://jsonapi.org/
What I mean is if we take one of the examples on GraphQL
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
Have a representation that would be more along these lines
{
hero {
name
# Queries can have comments!
friends {
id
}
},
friends {
id, name
}
}
Is that at all possible in GraphQL
Thanks
It is possible, and there's nothing wrong with having a friends field. In GraphQL terms you can have the following part of the schema:
type User {
id: ID
name: String
firends: [User]
}
type RootQuery {
hero: User
friends(forUserId: ID!): [User]
}
And then you can query this as you like – you can ask for friends separately:
{
friends(forUserId: "12") {
id, name
}
}
But the whole idea of GraphQL is that you don't have to do multiple queries to get the information you need. If you just need a list of users – that's a reasonable query, that most people have (with arguments for pagination and so on). With that said, there's no reason to fetch a list of IDs and to send another fetch query for the data right after that.

Resources