How to implement a filter on a query in Apollo? - graphql

I'm attempting to filter a query by a specific field. I can achieve this in Apollo explorer in dev tools but I can't seem to translate this into code.
The following works in Apollo explorer:
query ListUsersByType($filter: TableUsersFilterInput) {
listUsers(filter: $filter) {
items {
email
id
type
}
}
}
{
"filter": {
"type": {
"eq": "ADMIN"
}
}
}
I am unsure how this translates to the code using the useQuery hook however.
When I try the following it doesn't filter the list at all, it just fetches all of them regardless of type:
const ListUsersByType = gql`
query ListUsersByType($type: TableUsersFilterInput) {
listUsers(filter: $type) {
items {
email
id
type
}
}
}
`
const { data, loading, error } = useQuery(ListUsersByType, {
variables: {
filter: {
type: {
eq: 'ADMIN',
},
},
},
})
What am I missing here?

Your names are not correct
Here you say filter will use the variable type
const ListUsersByType = gql`
query ListUsersByType($type: TableUsersFilterInput) {
listUsers(filter: $type) {
items {
email
id
type
}
}
}
`
And here you pass filter
const { data, loading, error } = useQuery(ListUsersByType, {
variables: {
filter: {
type: {
eq: 'ADMIN',
},
},
},
})
You can
First solution
replace $type by $filter
const ListUsersByType = gql`
query ListUsersByType($filter: TableUsersFilterInput) {
listUsers(filter: $filter) {
items {
email
id
type
}
}
}
`
Second solution
rename the variable filter to type
const { data, loading, error } = useQuery(ListUsersByType, {
variables: {
type: {
type: {
eq: 'ADMIN',
},
},
},
})
My opinion
I let you choose but the first option seems the best

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

Gatsby - runQuery inside createResolver using group

I would like to use createResolvers to run a query using group, but I don't think this is possible at the moment. According to Accessing Gatsby’s data store from field resolvers, it seems runQuery currently accepts filter and sort query arguments, but not group. I was hopeful that runQuery would support group (based on https://github.com/gatsbyjs/gatsby/issues/15453), but I can't find any examples of grouping using runQuery
If possible, how can I use createResolvers to run a query using group for the following graphql query?
query {
allFile(filter: { internal: { mediaType: { eq: "text/markdown" } } }) {
group(field: sourceInstanceName) {
fieldValue
totalCount
}
}
}
For context, I have 2 folders (coding & recipes) that I am sourcing markdown files from. Here is my gatsby-config.js:
const path = require('path')
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `coding`,
path: path.resolve(__dirname, `src/contents/coding`),
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `recipes`,
path: path.resolve(__dirname, `src/contents/recipes`),
},
},
{
resolve: `gatsby-transformer-remark`,
},
],
}
Currently, I am able to use runQuery to run queries using filter. Here is my gatsby-node.js:
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
Query: {
recipes: {
type: `[File!]!`,
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
internal: { mediaType: { eq: 'text/markdown' } },
sourceInstanceName: { eq: 'recipes' },
},
},
type: `File`,
firstOnly: false,
})
},
},
coding: {
type: `[File!]!`,
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
internal: { mediaType: { eq: 'text/markdown' } },
sourceInstanceName: { eq: 'coding' },
},
},
type: `File`,
firstOnly: false,
})
},
},
},
})
}
But now I want to use createResolver to runQuery using group. Is this possible? If so, how?
I've produced a minimal repo at https://github.com/kimbaudi/gatsby-group-query
Currently, the home page (src/pages/index.jsx) is displaying the folders (coding & recipes) and the count of markdown files in that folder:
using the following page query:
export const query = graphql`
query {
allFile(filter: { internal: { mediaType: { eq: "text/markdown" } } }) {
group(field: sourceInstanceName) {
fieldValue
totalCount
}
}
}
`
and I would like to create a resolver that groups and use it in place of the above page query.

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"

GraphQL Subscriptions return an empty (null) response [duplicate]

I have the following GRAPHQL subscription:
Schema.graphql
type Subscription {
booking: SubscriptionData
}
type SubscriptionData {
booking: Booking!
action: String
}
And this is the resolver subsrciption file
Resolver/Subscription.js
const Subscription = {
booking: {
subscribe(parent, args, { pubsub }, info) {
return pubsub.asyncIterator("booking");
}
}
};
export default Subscription;
Then I have the following code on the Mutation in question
pubsub.publish("booking", { booking: { booking }, action: "test" });
I have the follow subscription file in front end (React)
const getAllBookings = gql`
query {
bookings {
time
durationMin
payed
selected
activity {
name
}
}
}
`;
const getAllBookingsInitial = {
query: gql`
query {
bookings {
time
durationMin
payed
selected
activity {
name
}
}
}
`
};
class AllBookings extends Component {
state = { allBookings: [] }
componentWillMount() {
console.log('componentWillMount inside AllBookings.js')
client.query(getAllBookingsInitial).then(res => this.setState({ allBookings: res.data.bookings })).catch(err => console.log("an error occurred: ", err));
}
componentDidMount() {
console.log(this.props.getAllBookingsQuery)
this.createBookingsSubscription = this.props.getAllBookingsQuery.subscribeToMore(
{
document: gql`
subscription {
booking {
booking {
time
durationMin
payed
selected
activity {
name
}
}
action
}
}
`,
updateQuery: async (prevState, { subscriptionData }) => {
console.log('subscriptionData', subscriptionData)
const newBooking = subscriptionData.data.booking.booking;
const newState = [...this.state.allBookings, newBooking]
this.setState((prevState) => ({ allBookings: [...prevState.allBookings, newBooking] }))
this.props.setAllBookings(newState);
}
},
err => console.error(err)
);
}
render() {
return null;
}
}
export default graphql(getAllBookings, { name: "getAllBookingsQuery" })(
AllBookings
);
And I get the following response:
data: {
booking: {booking: {...} action: null}}
I get that I am probably setting up the subscription wrong somehow but I don't see the issue.
Based on your schema, the desired data returned should look like this:
{
"booking": {
"booking": {
...
},
"action": "test"
}
}
The first booking is the field on Subscription, while the second booking is the field on SubscriptionData. The object you pass to publish should have this same shape (i.e. it should always include the root-level subscription field).
pubsub.publish('booking', {
booking: {
booking,
action: 'test',
},
})

GraphQL how to mutate data

I have a basic schema for mutating some data which looks like
const schema = new graphql.GraphQLSchema({
mutation: new graphql.GraphQLObjectType({
name: 'Remove',
fields: {
removeUser: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve(_, args) {
const removedData = data[args.id];
delete data[args.id];
return removedData;
},
},
},
})
});
Looking around google I cant find a clear example of the example query which needs to be sent to mutate.
I have tried
POST -
localhost:3000/graphql?query={removeUser(id:"1"){id, name}}
This fails with error:
{
"errors": [
{
"message": "Cannot query field \"removeUser\" on type \"Query\".",
"locations": [
{
"line": 1,
"column": 2
}
]
}
]
}
In order to post requests from the front-end application it is recommended to use apollo-client package. Say i wanted to validate a user login information:
import gql from 'graphql-tag';
import ApolloClient, {createNetworkInterface} from 'apollo-client';
client = new ApolloClient({
networkInterface: createNetworkInterface('http://localhost:3000/graphql')
});
remove(){
client.mutate({
mutation: gql`
mutation remove(
$id: String!
) {
removeUser(
id: $id
){
id,
name
}
}
`,
variables: {
id: "1"
}
}).then((graphQLResult)=> {
const { errors, data } = graphQLResult;
if(!errors && data){
console.log('removed successfully ' + data.id + ' ' + data.name);
}else{
console.log('failed to remove');
}
})
}
More information about apollo-client can be found here
Have you tried using graphiql to query and mutate your schema?
If you'd like to create a POST request manually you might wanna try to struct it in the right form:
?query=mutation{removeUser(id:"1"){id, name}}
(Haven't tried POSTing myself, let me know if you succeeded, i structured this out of the url when using graphiql)
You have to explicitly label your mutation as such, i.e.
mutation {
removeUser(id: "1"){
id,
name
}
}
In GraphQL, if you leave out the mutation keyword, it's just a shorthand for sending a query, i.e. the execution engine will interpret it as
query {
removeUser(id: "1"){
id,
name
}
}
cf. Section 2.3 of the GraphQL Specification
const client = require("../common/gqlClient")();
const {
createContestParticipants,
} = require("../common/queriesAndMutations");
const gql = require("graphql-tag");
const createPartpantGql = async (predictObj) => {
try {
let resp = await client.mutate({
mutation: gql(createContestParticipants),
variables: {
input: {
...predictObj,
},
},
});
let contestParticipantResp = resp.data.createContestParticipants;
return {
success: true,
data: contestParticipantResp,
};
} catch (err) {
console.log(err.message)
console.error(`Error creating the contest`);
return {
success: false,
message: JSON.stringify(err.message),
};
}
};

Resources