How to write graphql query wiith custom objects - graphql

The server side of graphql is with nodejs and express. This is the schema for graphql. It has one query which accepts DateT object having from and to dates.
var schema = buildSchema(`
type Query {
courseWithDate(
timeFilter: DateT
): Course
},
type Course {
...
from: String
to: String
},
type DateT{
from : String
to : String
}
`);
and this is how I am getting courses
I am able to run the application with this url
localhost:4000/graphql
This is the query I am using
query courseWithDate($from: dateFrom, $to: dateTo) {
courseWithDate(timeFilter: {
from: "${dateFrom}"
to: "${dateTo}"
}) {
title
...
}
}
with these parameters
{
"from": "2019-10-10","to":"2019-10-10"
}
Exception message I get is related to the input type I am trying to pass.
{
"errors": [
{
"message": "The type of Query.courseWithDate(timeFilter:) must be Input Type but got: DateT.",
"locations": [
{
"line": 6,
"column": 25
}
]
}
]
}

I'm not sure, but probably this style looks more like best practice
type Course {
id: Int
title: String
author: String
from: String
to: String
description: String
topic: String
url: String
}
input DateInput {
dateFrom: String!
dateTo: String!
}
type Query {
courseWithDate(input: DateInput!, name: String!): Course
}
And Query on client side should be:
{
courseWithDate(input: {
dateFrom: "${dateFrom}"
dateTo: "${dateTo}"
}
name: "${name}")
{
id
name
}
}

Related

How to pass nested variables to the GraphQL query in Apollo?

Trying to pass nested variables to the GraphQL query but my server gets only top-level variables (shopId), everything else is null.
I tried:
#1
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice(
$shopId: String!
$address1: String
$zip: String
$city: String
$countryCode: String
) {
calculatePackagePrice(
where: {
shopId: $shopId
destination: {
address1: $address1
zip: $zip
city: $city
countryCode: $countryCode
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
)
And #2:
export function CALCULATE_PACKAGE_PRICE({ shopId, destination }) {
return gql`
query CalculatePackagePrice {
calculatePackagePrice(
where: {
shopId: "${shopId}"
destination: {
address1: "${destination.address1}"
zip: "${destination.zip}
city: "${destination.city}"
countryCode: "${destination.countryCode}"
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
}
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE({
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
})
)
It works just fine when I hardcoded variables content to the queries. What I'm doing wrong?
Here is a helpful snippet from graphql docs,
All declared variables must be either scalars, enums, or input object types. So if you want to pass a complex object into a field, you need to know what input type that matches on the server.
You're correctly passing in the variables as strings, but then trying (perhaps successfully, but I've never seen the syntax before) to create the object in the gql template string. Instead, create an input type for destination and where.
input WhereInput {
shopId: String!
destination: DestinationInput!
}
input DestinationInput {
address1: String!
zip: String!
city: String!
countryCode: String!
}
then change the query on the client (and update the server definition),
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice($where: WhereInput!) {
calculatePackagePrice(where: $where) {
name
price
userErrors {
field
message
}
}
}
`
then pass the variables like,
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
where: {
shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
}
)

Using multiple mutations in one call

I have written my first script that utilises GraphQL (Still a learning curve)
Currently i am making 3 calls using GraphQL,
First is a product lookup,
Second is a Price Update,
Third is a Inventory Update.
To reduce the number of calls to the end point i wanted to merge both Price update and Inventory, But i am having 0 luck, i dont know if its bad formatting.
Here is my GraphQL Code (I am using Postman to help ensure the schema is correct before taking it to PHP)
mutation productVariantUpdate($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
product {
id
}
productVariant {
id
price
}
userErrors {
field
message
}}
second: inventoryActivate($inventoryItemId: ID!, $locationId: ID!, $available: Int) {
inventoryActivate(inventoryItemId: $inventoryItemId, locationId: $locationId, available: $available) {
inventoryLevel {
id
available
}
userErrors {
field
message
}
}
}
}
Variables:
{
"inventoryItemId": "gid://shopify/InventoryItem/XXXXXXXXXXX",
"locationId": "gid://shopify/Location/XXXXXXXXXX",
"available": 11 ,
"input": {
"id": "gid://shopify/ProductVariant/XXXXXXXXX",
"price": 55
}
}
Error i keep getting:
{
"errors": [
{
"message": "Parse error on \"$\" (VAR_SIGN) at [29, 29]",
"locations": [
{
"line": 29,
"column": 29
}
]
}
]
}
The way that you'd go about this is by specifying all your arguments at the root of your mutation, just like you did for ProductVariantInput:
mutation batchProductUpdates(
$input: ProductVariantInput!
$inventoryItemId: ID!
$locationId: ID!
$available: Int
) {
productVariantUpdate(input: $input) {
product { id }
productVariant { id price }
...
}
inventoryActivate(
inventoryItemId: $inventoryItemId
locationId: $locationId
available: $available
) {
inventoryLevel { id available }
...
}
}
Here's an example how this would work if you were to use fetch in JavaScript:
fetch("https://example.com/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `
mutation MyMutation($firstId: Int, $secondId: Int) {
m1: ToggleLike(id: $firstId) {
id
}
m2: ToggleLike(id: $secondId) {
id
}
}
`,
variables: {
firstId: 1,
secondId: 2
}
})
})
Hope this helps.

GraphQL: A schema must have a query operation defined

My IDE (Phpstorm with JS GraphQL) is giving me the title error for my schema.
I'm new to GraphQL, what should the query be set to if the actual query operation only has a mutation at the root level?
Below is an actual query taken out of a (Shopify) tutorial for their GraphQL API. I'm copying my local schema definition below which attempted to accommodate its shape.
As you can see, The query is entirely nested in a mutation so I don't know what a query definition at the root level should even have.
// graphql.ts
import "isomorphic-fetch";
const buildPricingPlanQuery = (redirectUrl: string) => `mutation {
appSubscribeCreate(
name : "Plan 1"
returnUrl : "${redirectUrl}"
test : true
lineItems : [
{
plan : {
appUsagePricingDetails : {
cappedAmount : {
amount : 10
, currencyCode : USD
}
terms : "Up to 50 products"
}
}
}
{
plan : {
appRecurringPricingDetails : {
price : {
amount : 10
, currencyCode : USD
}
terms : "some recurring terms"
}
}
}
]
)
{
userErrors {
field
message
}
confirmationUrl
appSubscription {
id
}
}
}`;
export const requestSubscriptionUrl = async (ctx: any, accessToken: string, shopDomain: string) => {
const requestUrl = `https://${shopDomain}/admin/api/2019-10/graphql.json`;
const response = await fetch(requestUrl, {
method : 'post'
, headers : {
'content-type' : "application/json"
, 'x-shopify-access-token' : accessToken
},
body : JSON.stringify({query: buildPricingPlanQuery(`https://${shopDomain}`)})
});
const responseBody = await response.json();
const confirmationUrl = responseBody
.data
.appSubscriptionCreate
.confirmationUrl;
return confirmationUrl;
};
// pricingSchema.graphql
# ------------ Minor Types
enum CurrencyCode {
USD
EUR
JPY
}
type cappedAmount {
amount: Int
currencyCode : CurrencyCode
}
type appUsagePricingDetails {
cappedAmount: cappedAmount
}
input PlanInput {
appUsagePricingDetails: cappedAmount
terms: String
}
type userErrors {
field: String
message: String
}
type appSubscription {
id: Int
}
# ------------ Major Type and Schema definition
type PricingPlan {
appSubscribeCreate(
name: String!
returnUrl: String!
test: Boolean
lineItems: [PlanInput!]!
): String
userErrors: userErrors
confirmationUrl: String
appSubscription: appSubscription
}
schema {
mutation: PricingPlan
}
The error you see is referring to this stipulation of the GraphQL specification:
The query root operation type must be provided and must be an Object type.
There have been a couple proposals to remove this restriction, but as of the latest (June 2018) spec, a schema is considered invalid if there is no Query type. The spec also states that Object types (including Query) cannot be empty.
My advice: Just add a simple query type, such as
type Query {
ping: String #deprecated(reason: "https://stackoverflow.com/questions/59868942/graphql-a-schema-must-have-a-query-operation-defined")
}
If the spec gets updated, you can remove it later :)

How to set the Apollo GraphQL server to accept an object as a variable to a mutation?

Currently, I'm trying to pass an object as a variable to a mutation as shown below:
type ShopLocation {
lane1: String
lane2: String
city: String
postalCode: String
country: String
}
type ShopResponse {
statusCode: Int
messageCode: String
data: String
}
type Mutation {
createShop(
name: String
email: String
location: ShopLocation
): ShopResponse
}
But I get the following error:
{
"error": {
"errors": [
{
"message": "The type of Mutation.createShop(location:) must be Input Type but got: ShopLocation.",
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED",
"exception": {
"stacktrace": [
"Error: The type of Mutation.createShop(location:) must be Input Type but got: ShopLocation.",
" at assertValidSchema (.../node_modules/graphql/type/validate.js:71:11)",
" at Object.validate (.../node_modules/graphql/validation/validate.js:55:35)",
" at Promise.resolve.then (.../node_modules/apollo-server-core/src/runQuery.ts:188:30)",
" at <anonymous>",
" at process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
}
}
]
}
}
Any idea how to properly do this?
You need to make the ShopLocation with input keyword instead of type,
input ShopLocationInput {
lane1: String
lane2: String
city: String
postalCode: String
country: String
}
type ShopResponse {
statusCode: Int
messageCode: String
data: String
}
type Mutation {
createShop(
name: String
email: String
location: ShopLocationInput
): ShopResponse
}

What does the GraphQL schema/resolver look like for an object of objects?

data
{
user_id: 'abc',
movies: {
'111': {
title: 'Star Wars 1'
},
'112': {
title: 'Star Wars 2'
}
}
}
What would the schema and resolver for this look like?
This was my best attempt, but I've never seen an example like this, so really not sure.
schema
type User {
user_id: String
movies: Movies
}
type Movies {
id: Movie
}
type Movie {
title: String
}
resolver
User: {
movies(user) {
return user.movies;
}
},
Movies: {
id(movie) {
return movie;
}
}
You're still missing a Query type, which tells GraphQL where your queries can start. Something like:
type Query {
user(id: String): User
movie(id: String): Movie
}
also, I think for your movies, you should have [Movie] instead of a Movies type and then id inside of it. So get rid of your Movies type and just do this:
type User {
id: String
movies: [Movie]
}
type Movie {
id: String
title: String
}

Resources