How to make pagination on alternative arguments (Apollo) - graphql

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!

Related

WpGraphQL query returns null

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

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]
};

Why does GraphQL require me to have a resolver that returns an empty object?

So, when I have a schema:
type Query { me: Me }
type Me { hello: String }
I expect a satisfying resolver to be:
const resolvers = {
Me: {
hello() { return "Hi, me!"; }
}
}
Alas, this isn't the case, I have to add a dummy me-resolver (see below).
Why is this the case?
I'd say it should traverse the query, and if it can't find a satisfying field-resolver, it should look for a corresponding type-resolver.
const { graphql } = require("graphql");
const { makeExecutableSchema } = require("graphql-tools");
const compose = require("compose-function");
const typeDefs = `
type Query {
me: Me
}
type Me {
hello: String!
}
`;
const resolvers = {
Query: {
// ===========================>
me() {
return {};
}
// <===========================
},
Me: {
hello() {
return "Hi, me!";
}
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers
});
graphql(schema, "{ me { hello } }").then(
compose(
console.log,
JSON.stringify
)
); // {"data":{"me":{"hello":"Hi, me!"}}}
Alright, I've figured it out. It actually makes a lot of sense.
null is a valid response. Only if there is an object with properties that the query wants, it should look for the next resolver to satisfy the query.
It's also written in the spec under Errors and Non‐Null Fields https://graphql.github.io/graphql-spec/June2018/#sec-Executing-Selection-Sets

Resources