WpGraphQL query returns null - graphql

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");
}

Related

How to make pagination on alternative arguments (Apollo)

I have two arguments (skip and take) and they are different from standart "offset" and "limit".
How do I make this scheme work:
function App() {
const { networkStatus, error, data, fetchMore } = useQuery(ALL_LINKS, { variables: { take: 30, skip: 0 } });
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
feed: offsetLimitPagination()
}
}
}
});
export const ALL_LINKS = gql`
query AllLinks ($skip: Int, $take: Int) {
feed ( skip: $skip, take: $take ) {
links {
id
}
}
}`;
Custom name for arguments inside offsetLimitPagination(["skip", "take"]) doesn`t work.
An empty array is returned to me.
Thanks for answer!

Variable "$productSlug" is never used in operation "SingleProduct". Graphql error. Variables are not working in my Gatsby graphql queries

I am getting this error: Variable "$productSlug" is never used in operation "SingleProduct".
I also use gatsby-source-wordpress to query fields from wordpress to gatsby. I also uninstalled Gatsby and re-installed it to see if it works in different versions, but it didn't.
I searched all over the internet and stack overflow for an answer, I really believe it must be a bug with Gatsby or gatsby-source-wordpress,
this is the code:
const path = require("path");
const { createFilePath } = require(`gatsby-source-filesystem`);
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions;
if (page.path.match(/products/)) {
page.context.layout = "ArchiveProduct";
createPage(page);
}
if (page.path.match(/products\/([^\/]+$)/)) {
page.context.layout = "SingleProduct";
createPage(page);
}
};
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions;
if (node.internal.type === `allWpProduct`) {
const slug = createFilePath({ node, getNode, basePath: `pages` });
createNodeField({
node,
name: `slug`,
value: slug,
});
}
};
exports.createPages = async function ({ graphql, actions }) {
const { data } = await graphql(`
query SingleProduct {
allWpProduct {
nodes {
uri
slug
id
}
}
}
`);
data.allWpProduct.nodes.forEach((node) => {
// const slug = node.slug;
actions.createPage({
path: "/products/" + node.slug,
component: path.resolve("./src/templates/SingleProduct.js"),
context: {
productSlug: node.slug,
productId: node.id,
layout: "SingleProduct",
},
});
});
};
And this is the query:
export const query = graphql`
query SingleProduct($productSlug: String!) {
wpProduct(slug: { eq: "$productSlug" }) {
title
slug
link
id
date(formatString: "MMMM DD, YYYY")
product {
description
price
slug
photo {
localFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
`;
Try the following:
export const query = graphql`
query SingleProduct($productSlug: String!) {
wpProduct(filter: {slug: { eq: "$productSlug" }}) {
title
slug
link
id
date(formatString: "MMMM DD, YYYY")
product {
description
price
slug
photo {
localFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
`;
Your issue appears because $productSlug is lifted properly via context but never used in any sort of filtering action inside the query.
I'd recommend you check it before in the GraphiQL playground hardcoding the $productSlug to check the output.

Can I make my graphql query multipurpose?

I would like to query products by different filters and criteria so I have written multiple queries for my frontend for each case (shown below). Is there a way I can write and use one "multipurpose" query instead of these?
const GET_PRODUCTS = gql`
query {
products {
...productFragment
}
}
${PRODUCT_FRAGMENT}
`
const GET_PRODUCTS_BY_PRICE = gql`
query($sortFilter: String) {
products(sort: $sortFilter) {
# (sort: "price:asc") or (sort: "price:desc")
...productFragment
}
}
${PRODUCT_FRAGMENT}
`
const GET_PRODUCTS_BY_CATEGORY = gql`
query($categoryId: String) {
products(where: { categories: { id: $categoryId } }) {
...productFragment
}
}
${PRODUCT_FRAGMENT}
`
const GET_PRODUCTS_BY_CATEGORY_AND_PRICE = gql`
query($sortFilter: String, $categoryId: String) {
products(sort: $sortFilter, where: { categories: { id: $categoryId } }) {
...productFragment
}
}
${PRODUCT_FRAGMENT}
`
Looks like I can write a helper fn like this then:
function getRequiredProductsQuery({ sortFilter, categoryId }) {
if (sortFilter && categoryId) {
return { key: 'PRODUCTS_BY_CATEGORY_AND_PRICE', query: GET_PRODUCTS_BY_CATEGORY_AND_PRICE }
}
if (sortFilter) {
return { key: 'PRODUCTS_BY_PRICE', query: GET_PRODUCTS_BY_PRICE }
}
if (categoryId) {
return { key: 'PRODUCTS_BY_CATEGORY', query: GET_PRODUCTS_BY_CATEGORY }
}
return { key: 'PRODUCTS', query: GET_PRODUCTS }
}
Is it really all necessary?
ok, I figured that I can provide default params like $categoryId: String = "id:asc"

Apollo nodejs server; How to get mutation/query schema path in the request context when writing a plugin?

I'm writing an Apollo server plugin for node.js, and my goal is to improve my teams debugging experience. My plugin currently looks something like this:
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
willSendResponse(context) { // <== Where do I find the "path" in the schema here?
// Inspired by this: https://blog.sentry.io/2020/07/22/handling-graphql-errors-using-sentry
// and the official documentation here: https://docs.sentry.io/platforms/node/
// handle all errors
for (const error of requestContext?.errors || []) {
handleError(error, context);
}
},
};
},
};
}
I would like to know if I can access the path in the schema here? It's pretty easy to find the name of mutaiton/query with operation.operationName, but where can I get the name of the query/mutation as defined in the schema?
Solution
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
didResolveOperation(context) {
const operationDefinition = context.document
.definitions[0] as OperationDefinitionNode;
const fieldNode = operationDefinition?.selectionSet
.selections[0] as FieldNode;
const queryName = fieldNode?.name?.value;
// queryName is what I was looking for!
},
};
},
};
}
Your requirement is not very clear. If you want to get the name of the query/mutation to distinguish which query or mutation the client sends.
You could get the name from context.response.data in willSendResponse event handler.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
import { parse, OperationDefinitionNode, FieldNode } from 'graphql';
function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
return {
didResolveOperation(context) {
console.log('didResolveOperation');
const obj = parse(context.request.query!);
const operationDefinition = obj.definitions[0] as OperationDefinitionNode;
const selection = operationDefinition.selectionSet.selections[0] as FieldNode;
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, selection.name.value);
},
willSendResponse(context) {
console.log('willSendResponse');
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, Object.keys(context.response.data!)[0]);
},
};
},
};
}
const typeDefs = gql`
type Query {
hello: String
}
type Mutation {
update: String
}
`;
const resolvers = {
Query: {
hello() {
return 'Hello, World!';
},
},
Mutation: {
update() {
return 'success';
},
},
};
const server = new ApolloServer({ typeDefs, resolvers, plugins: [eddyApolloPlugin()] });
const port = 3000;
server.listen(port).then(({ url }) => console.log(`Server is ready at ${url}`));
GraphQL Query:
query test {
hello
}
the logs of the server:
didResolveOperation
operationName: test
query name: hello
willSendResponse
operationName: test
query name: hello
GraphQL Mutation:
mutation test {
update
}
the logs of the server:
didResolveOperation
operationName: test
mutation name: update
willSendResponse
operationName: test
mutation name: update

how to pass a variable to graphql query?

I have a question.
There is a function that returns the result of a graphql query. In this function, I want to pass the arguments that will be used in the request, for example, to control order direction.
How can i do this?
1) Fucntion query
import { useStaticQuery, graphql } from "gatsby";
export const useSiteMetadata = (dir) => {
const {allContentfulBlogPost:{edges}} = useStaticQuery(
graphql`
query {
allContentfulBlogPost(
sort:{
fields: published,
order: $dir
}
){
edges{
node{
name,
alias,
published,
id
}
}
}
}
`
);
return edges;
};
2)Function call
const res = useSiteMetadata('ASC');
exempel code
import { useStaticQuery, graphql } from "gatsby";
export const useSiteMetadata = (dir) => {
const { allPostsAsc, allPostsDsc } = useStaticQuery(
graphql`
query {
allPostsAsc: allContentfulBlogPost(sort: { fields: published, order: "ASC" }) {
edges {
node {
name
alias
published
id
}
}
}
allPostsDsc: allContentfulBlogPost(sort: { fields: published, order: "DSC" }) {
edges {
node {
name
alias
published
id
}
}
}
}
`
);
const posts = {
ASC: allPostsAsc,
DSC: allPostsDsc
}
return posts[dir]
};

Resources