How I can skip request/query in useLazyLoadQuery.
import { useLazyLoadQuery } from 'react-relay/hooks';
const id = props.selectedId // id can be a number or null
const user = useLazyLoadQuery(query, {id}) // skip query/request network if id is null
You can use the #skip or #include directive. If the query ends up being empty following the directive conditions, no network request will be made. Consider the example:
import { useLazyLoadQuery } from 'react-relay/hooks';
function MaybeUser(props) {
const { userId } = props;
// 👆 is optional/nullable
useLazyLoadQuery(
graphql`
query MaybeUserQuery($userId: ID!, $skip: Boolean!) {
user(id: $userId) #skip(if: $skip) {
fullName
}
}
`,
{
userId: userId || '', // we need to pass something because of the query $userId type decleration
skip: !userId, // skip when there is no user ID
},
);
return <magic />;
}
Empty userId produces an empty MaybeUserQuery resulting in no network requests.
Related
I have a nested query (query inside query) with apollo client.
Everything works great, I do the request and get the correct data, but the issue is when I'm trying to use the cache, the cache returns undefined for the nested query prop.
My query:
query GetStudents($first: Int!, $after: String) {
me {
id
email
firstName
lastName
students(first: $first, after: $after) {
edges {
node {
id
created
number
status
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
When I try to use the inMemoryCache, the students is always undefined :
new InMemoryCache({
typePolicies: {
Query: {
fields: {
me: {
keyArgs: false,
merge(existing = {}, incoming = {}, { readField }) {
const id = readField("id", incoming);
const email = readField("email", incoming);
const students = readField("students", incoming);
return {
...
};
},
},
}
}
}
});
I can read correctly the id and email from the cache, but the students (which is the nested query) will be always undefined.
Do I need to read the cache students in a different way because it is a query?
I'm having this GraphQL query from headless Wordpress in Nexjs via WpGraphQl plugin:
export const GET_POSTS_BY_CATEGORY_SLUG = gql`
query GET_POSTS_BY_CATEGORY_SLUG( $slug: String, $uri: String, $perPage: Int, $offset: Int ) {
${HeaderFooter}
page: pageBy(uri: $uri) {
id
title
content
slug
uri
seo {
...SeoFragment
}
}
categories(where: {slug: $slug}) {
edges {
node {
slug
posts: posts(where: { offsetPagination: { size: $perPage, offset: $offset }}) {
edges {
node {
id
title
excerpt
slug
featuredImage {
node {
...ImageFragment
}
}
}
}
pageInfo {
offsetPagination {
total
}
}
}
}
}
}
}
${MenuFragment}
${ImageFragment}
${SeoFragment}
`;
And this is my getStaticProps function:
export async function getStaticProps(context) {
const { data: category_IDD } = await client.query({
query: GET_POSTS_BY_CATEGORY_SLUG,
});
const defaultProps = {
props: {
cat_test: JSON.parse(JSON.stringify([category_IDD])),
},
revalidate: 1,
};
return handleRedirectsAndReturnData(defaultProps, data, errors, "posts");
}
If i pass it like this in props:
const defaultProps = {
props: {
cat_test: category_IDD,
},
i get an error saying:
SerializableError: Error serializing `.cat_test` returned from `getStaticProps` in "/category/[slug]". Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
But when i JSON.parse as the code above, i get null
Whats wrong with this query?
Just noticed that the $slug is an array of strings, so here should be:
query GET_POSTS_BY_CATEGORY_SLUG( $slug: [String], $uri: String, $perPage: Int, $offset: Int )
instead of $slug: String
You're not actually passing the $slug variable to the query.
For instance if your page route is /category/[slug].js your getStaticProps should look something like this.
export async function getStaticProps(context) {
const { slug } = context.params;
const { data: category_IDD } = await client.query({
query: GET_POSTS_BY_CATEGORY_SLUG,
variables: { slug },
});
const defaultProps = {
props: {
cat_test: JSON.parse(JSON.stringify([category_IDD])),
},
revalidate: 1,
};
return handleRedirectsAndReturnData(defaultProps, data, errors, "posts");
}
I have a graphql delete mutation that works perfectly with Strapi in the graphql plugin playground.
In a Vuejs frontend I cannot get the mutation to read the id of the item to delete.
The query in the graphql playground is
mutation deleteInvoice ($id: deleteInvoiceInput!) {
deleteInvoice(input: $id) {
invoice {
id
}
}
}
Query variables
{ "id": "618bacab6a2a1b2f7407a8e8" }
The frontend display is a list with button to delete each item
<tr v-for="(item, id) in result.invoices" :key="id" id="delete-item">
<td>{{ item.id }}</td>
<button #click="onDeleteClicked(item)" uk-icon="icon: trash"></button>
</td>
const { mutate: deleteInvoice } = useMutation(gql`
mutation deleteInvoice($id: ID!) {
deleteInvoice(input: { where: { id: $id } }) {
invoice {
id
}
}
}`,
(id) => ({
variables: { id }
})
)
const onDeleteClicked = (item) => {
deleteInvoice({ variables: { item: item.id} })
alert(item.id)
}
the alert displays correctly the id of the item clicked - but the id is not passed the mutation
how to pass the id of the item clicked to the mutation?
The variables object must contain key value pairs where the key matches a declared variable. In your case you declare the variable id but in the object you use the key invoiceID. Either rename the variable or the key.
Furthermore, you are probably confusing yourself by calling the variable id, even though it expects a filter input like above: { where: { id: "id value" } }. The easiest way is probably to not pass in the object from JavaScript, but make your query take a variable of type ID:
mutation deleteInvoice ($id: ID!) {
deleteInvoice(input: { where: { id: $id } }) {
invoice {
id
}
}
}
Now this makes mutch more sense as id is really an ID and not a deleteInvoiceInput.
Bringing it all together:
const [deleteInvoice] = useMutation(gql`
mutation deleteInvoice($id: ID!) {
deleteInvoice(input: { where: { id: $id } }) {
invoice {
id
}
}
}
`)
// Usually in callback
const onDeleteClicked = (event) => {
const id = event.currentTarget.getAttribute('data-id');
deleteInvoice({ variables: { id } });
}
I want to access the country field from my resolver. The country is being returned by query but since Product is a list I can only access the object inside items return by query. Is there any way I can have access to whole returned data from query or any way to pass it further down as an argument to my resolver function
//schema
type ProductCollectionPage {
items: [Product!]!
}
//resolver
const resolvers = {
Product: {
variants: async (obj: any, args: any, { dataSources }: any): Promise<IProductVariantPage> => {
const { id } = obj;
// want to access country here
return (dataSources.xyz as XyzRepository).retriveProducts(country, id);
}
},
Query: {
products: async (
obj: any,
{ id }: { id: string },
{ dataSources }: any
): Promise<
any
> => {
const locationDetails = await (dataSources.abc as InventoryLocationsRepository).retrieveInventoryLocation(id);
const country = locationDetails.country;
const response = await (dataSources.abc as XyzRepository).retriveProductIds(country);
// response.list === [{id: 1}, {id:2}]
return {
country,
items: response.list
}
}
}
};
As arrays are objects in javascript then you can just assign additional property to response.list:
response.list.country = country;
I'm studying GraphQL Mutation components. I'm doing a mutation that adds a resolution, i.e. a New Year's resolution. Here's the schema:
type Resolution {
_id: String!
name: String!
goals: [Goal]
completed: Boolean
}
type Query {
resolutions: [Resolution]
}
type Mutation {
createResolution(name: String!): {
Resolution
user: String
}
}
Here are the resolution resolvers:
import Resolutions from "./resolutions";
import Goals from "../goals/goals";
import { PubSub } from 'graphql-subscriptions';
export const pubsub = new PubSub();
export default {
Query: {
resolutions(obj, args, { userId }) {
return Resolutions.find({
userId
}).fetch();
}
},
Resolution: {
goals: resolution =>
Goals.find({
resolutionId: resolution._id
}).fetch(),
completed: resolution => {
const goals = Goals.find({
resolutionId: resolution._id
}).fetch();
if (goals.length === 0) return false;
const completedGoals = goals.filter(goal => goal.completed);
return goals.length === completedGoals.length;
}
},
Mutation: {
createResolution(obj, { name }, { userId }) {
if (userId) {
const resolutionId = Resolutions.insert({
name,
userId
});
return Resolutions.findOne(resolutionId);
}
throw new Error("Unauthortized");
}
},
};
Here's the user resolver:
export default {
Query: {
user(obj, args, { user }) {
return user || {};
}
},
User: {
email: user => user.emails[0].address
}
};
Here's the mutation component:
const ResolutionForm = () => {
let input;
let state = {
error: null
};
return (
<Mutation
mutation={CREATE_RESOLUTION}
update={(cache, {data: {createResolution}}) => {
const {resolutions} = cache.readQuery({query: GET_RESOLUTIONS});
cache.writeQuery({
query: GET_RESOLUTIONS,
data: {resolutions: resolutions.concat([createResolution])}
});
}}
>
{(createResolution, {data}) => (
<div>
<form
onSubmit={e => {
e.preventDefault();
createResolution({
variables: {
name: input.value
},
});
input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Submit</button>
</form>
</div>
)}
</Mutation>
);
};
Here's the query that loads all the resolutions when the app launches:
const GET_RESOLUTIONS = gql`
query Resolutions {
resolutions {
_id
name
completed
goals {
_id
name
completed
}
}
user {
_id
}
}
`;
That works fine, but when I run the mutation:
const CREATE_RESOLUTION = gql`
mutation createResolution($name: String!) {
createResolution(name: $name) {
__typename
_id
name
goals {
_id
name
completed
}
completed
}
}
`;
...I get a console log error saying:
Missing field user in {
"resolutions": [
{
"_id": "GKTNgbuiDgiZ4wAFZ",
"name": "testing 123",
.....
How do I get the field user into my mutation response?
The GET_RESOLUTIONS query used is originally from a parent component, App.js. It really contains two separate queries-- one for the resolution and one for the user. The CREATE_RESOLUTION Mutation query and resolver, don't return user data, and I don't yet know how to get them to do that.
But, the Mutation component doesn't need the user data. It only gets upset during the call to cache.writeQuery because GET_RESOLUTIONS is asking for user, and the Mutation resolver isn't returning user.
So the fix seems to be to have a special GET_RESOLUTIONS_FOR_MUTATION_COMPONENT query that doesn't ask for user in the first place:
const GET_RESOLUTIONS_FOR_MUTATION_COMPONENT = gql`
query Resolutions {
resolutions {
_id
name
completed
goals {
_id
name
completed
}
}
}
`;
[.....]
const {resolutions} = cache.readQuery({query: GET_RESOLUTIONS_FOR_MUTATION_COMPONENT});
[.....]
Using that there is no error message asking for user.