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;
Related
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've got a subscription query, MessageFolder_Subscription, that looks like this:
QUERY
const MESSAGEFOLDER_SUBSCRIPTION_QUERY = gql`
subscription ($localUserID: String!){
MessageFolder_Subscription(userID: $localUserID){
id
remoteUserData{
id
name_title
name_first
name_last
[...more fields...]
}
},
}
`;
Here's the schema for it:
SCHEMA
type myUserData {
id: String
gender: String
name_title: String
name_first: String
*[...more fields...]*
}
type messageFolder{
id: String
remoteUserData: myUserData
}
type Subscription {
MessageFolder_Subscription(userID: String!): messageFolder
}
Here's how I'm doing the resolvers:
RESOLVERS
const resolvers = {
//FIELD RESOLVER
MessageFolder_Subscription: {
subscribe: withFilter(
() => pubsub.asyncIterator(MSGFOLDER_ADDED_CHANNEL),
(payload, args) => {
debugger; <=== NEVER FIRES
if (typeof (payload) === 'undefined') {
return false;
}
let result = false;
const userId = Meteor.userId();
// let messageFolder = MessageFolder_Subscription.messageFolder;
result = (userId === args.fromID || args === MSGFOLDERargs.toID);
return result;
}
)
},
//ROOT RESOLVER
*[......more resolvers here.....]*
Subscription: {
MessageFolder_Subscription: {
subscribe: withFilter(
() => pubsub.asyncIterator(MSGFOLDER_ADDED_CHANNEL),
(payload, args) => {
debugger;
if (typeof (payload) === 'undefined') {
return false;
}
let result = false;
const userId = Meteor.userId();
// let messageFolder = MessageFolder_Subscription.messageFolder;
result = (userId === args.fromID || args === MSGFOLDERargs.toID);
return result;
}
)
}
}
When I mutate a related item, the MessageFolder_Subscription query is fired by pubsub as expected. Tracing through, I can see that it returns true.
But for some reason, the field resolver, for the field remoteUserData on MessageFolder_Subscription, never fires.
What am I missing?
Solved. I had to add the __typename: field:
const messageFolder_Subscription = {
__typename: 'messageFolder_Subscription',
id: userID,
}
...to the MessageFolder_Subscription subscription object, when it was created in the mutation resolver, prior to being passed to pubsub.
This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 3 years ago.
i am toying with the Star Wars API using GraphQL. Using GraphQL Playground, i get null values for the response for the joint entities.
I believe the reason is because of the organization of my schema and resolver files. Below are my codes and the files they are stored in, anyone can help? The current setup only returns the name of the Star Wars character but doesn't return the array of films details under the person/character
Thanks a lot
GQL Playground
{
"data": {
"getPerson": {
"name": "Obi-Wan Kenobi",
"films": [
{
"title": null,
"director": null
}
]
}
}
}
graphql/schema.ts
import { gql } from "apollo-server-express";
export const typeDefs = gql`
type Person {
name: String
height: String
mass: String
homeworld: Planet
films: [Film]
vehicles: [Vehicle]
}
type Planet {
name: String
diameter: String
climate: String
terrain: String
population: String
films: [Film]
}
type Film {
title: String
episode_id: Int
director: String
producer: String
releaseDate: String
}
type Vehicle {
name: String
model: String
manufacturer: String
length: String
crew: String
passengers: String
pilots: [Person]
}
type Query {
getPerson(id: Int!): Person
}
schema {
query: Query
}
`;
graphql/resolvers/index.ts
import PersonResolvers from "./person-resolvers";
export const resolvers = {
Query: {
getPerson: PersonResolvers.getPerson
}
};
graphql/person-resolvers.ts
import fetch from "node-fetch";
export default {
getPerson: async (_: any, { id }: { id: string }) => {
try {
const res = await fetch(`https://swapi.co/api/people/${id}/`);
return res.json();
} catch (error) {
throw error;
}
},
Person: {
films: (person: any) => {
const promises = person.films.map(async (url: string) => {
const res = await fetch(url);
return res.json();
});
return Promise.all(promises);
},
vehicles: (person: any) => {
const promises = person.vehicles.map(async (url: string) => {
const res = await fetch(url);
return res.json();
});
return Promise.all(promises);
}
},
Vehicle: {
pilots: (vehicle: any) => {
const promises = vehicle.pilots.map(async (url: string) => {
const res = await fetch(url);
return res.json();
});
return Promise.all(promises);
}
}
};
I have managed to get it work with this folder organization
For those looking for answers, u can check out my repo below
myhendry gql github repo
I've successfully built a GraphQL API which allows nested queries. Using the generic examples of Countries & States, I can perform a query like this:
query{
country(id:"Q291bnRyeTo0Nw==") {
states {
edges {
node {
id,
name,
area,
population
}
}
}
}
}
What I've discovered I can't seem to do is this:
query{
country(id:"Q291bnRyeTo0Nw==") {
state(id:"U3RhdGU6MzM=") {
edges {
node {
id,
name,
area,
population
}
}
}
}
}
Might there be a way with GraphQL to specify a specific parent and specific child in one query?
Robert
Update: For Daniel's benefit, here is my current GraphQL Query code:
from .models import Country as CountryModel
from .models import State as StateModel
class Query(graphene.AbstractType):
country = graphene.Field(Country, id=graphene.String())
countries = graphene.List(Country)
state = graphene.Field(State, id=graphene.String())
states = graphene.List(State)
def resolve_country(self, args, context, info):
id = args.get('id')
if id is not None:
return CountryModel.objects.get(id=Schema.decode(id))
return None
def resolve_countries(self, args, context, info):
return CountryModel.objects.all()
def resolve_state(self, args, context, info):
id = args.get('id')
if id is not None:
return StateModel.objects.get(id=Schema.decode(id))
return None
def resolve_states(self, args, context, info):
return StateModel.objects.all()
You'd need to define a resolver for both the country field on the Root Query and the state field on the Country type. Here's an example you can copy and paste into Launchpad and see it in action.
The set up for something like Graphene would be a little different, but the idea is the same: the object returned by your country query is made available to the resolver for every field under the state type. You use the id argument passed to the state field to filter the data on that object (in this example, the returned object has a states property) and return the appropriate state.
import { makeExecutableSchema } from 'graphql-tools';
const countries = [
{
id: 1,
name: 'bar',
states: [
{
name: 'foo',
id: 20
}
]
},
{ id: 2 },
];
const typeDefs = `
type Query {
country(id: Int!): Country
}
type Country {
id: Int
state(id: Int!): State
}
type State {
id: Int
name: String
}
`
const resolvers = {
Query: {
country: (obj, args, context) => {
return countries.find(country => country.id === args.id)
},
},
Country: {
state: (obj, args, context) => {
return obj.states.find(state => state.id === args.id)
},
}
}
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
})
Edit: Assuming the object returned by CountryModel.objects.get(id=Schema.decode(id)) includes a states attribute that is a list of states, you should be able to do something like:
class Country(graphene.ObjectType):
state = graphene.Field(State,
id=graphene.String()
)
# other fields
def resolve_state(self, args, context, info):
id = args.get('id')
if id is not None:
return list(filter(lambda x: x.id == id, self.states)
return None
I have mock data and GraphQL server setup as shown below
Please guide how to write resolver function with sub type e.g. data kept in JSON (database) as customerId but i want data to be return as Customer type
Thanks
Mock Order Type JSON Data
[
{
"id": 1,
"customerId": 145,
"productIds": [
136,
148,
102
]
}
]
GraphQL Server
const express = require('express')
const fs = require('fs')
const graphQLHTTP = require('express-graphql')
const { buildSchema } = require('graphql')
const db = require('./db.json')
const app = express()
const schema = buildSchema(`
type Customer {
id: Int
firstName: String
lastName: String
}
type Product {
id: Int
productName: String
}
type Order {
id: Int
customer: Customer
products: [Product]
}
type Query {
orders: [Order]
}
`)
const resolver = {
orders() {
return db['orders']
.map(order => {
const customer = db['customers'].find(c => c.id == order.customerId)
const products = order.productIds
.map(id => db['products'].find(p => p.id == id))
return { ...order, customer, products }
})
}
}
app.use(graphQLHTTP({
schema,
rootValue: resolver,
graphiql: true
}))
app.listen(3000)