GraphQL: A schema must have a query operation defined - graphql

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 :)

Related

What this 400 bad req is actually asking for ion my apollo client query?

I'm running this exact same query on apolloStudio and the response it's correct but in the code I'm getting a 400 bad req, I assume that the syntax is wrong, and it's quite complex given the fact that I'm querying an input inside a query. Here's an image of the response in apolloStudio:
Here's the code:
const { result, loading, error } = useQuery(
gql`
query GetUser(
$id: String
$ratingHistoryFilter: userRatingHistoryFilterInput
) {
getUser(_id: $id) {
_id
ratingHistory(ratingHistoryFilter: $ratingHistoryFilter) {
rating
date
}
}
}
`,
{
id: props.uid,
ratingHistoryFilter: {
dateFrom: '1994-06-24',
verified: 'false',
dateTo: '2022-07-06',
userID: {
condition: 'EQUAL',
value: '62c2d4100892074470ca374e'
}
}
}
)
console.log(result)
const userData = computed(() => result.value?.getUser ?? {})
console.log(userData.value)
Here's the console error:
This is the network response:
{"errors":[{"message":"Variable "$id" of type "String" used in position expecting type "String!".","code":"INTERNAL_SERVER_ERROR"},{"message":"Variable "$ratingHistoryFilter" of type "userRatingHistoryFilterInput" used in position expecting type "userRatingHistoryFilterInput!".","code":"INTERNAL_SERVER_ERROR"}]}

How to get field value having space, hyphen in REST API in GraphQL

REST API Endpoint - https://api.jikan.moe/v3/manga/13
"Alterantive version", "Side story" and "Spin-off" fields are having space and hyphen.
common_schema.js
const { gql } = require('apollo-server');
const typeDefs = gql`
type RelatedType {
Adaptation: [RelatedSubType]
SideStory: [RelatedSubType]
Character: [RelatedSubType]
Summary: [RelatedSubType]
Other: [RelatedSubType]
AlternativeVersion: [RelatedSubType]
SpinOff: [RelatedSubType]
}
type RelatedSubType {
mal_id: ID
type: String
name: String
url: String
}
`;
module.exports = typeDefs;
If I write field value as Spin-off or Alternative version then it gives an error in terminal. "Spin-off" also doesn't work. I know these aren't valid but then also tried.
manga_resolver.js
module.exports = {
Query: {
manga: (_, { id }, { dataSources }) =>
dataSources.mangaAPI.getMangaDetail(id)
}
};
manga.js
const { RESTDataSource } = require('apollo-datasource-rest');
class MangaAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://api.jikan.moe/v3/manga/';
}
async getMangaDetail(mal_id) {
const response = await this.get(`/${mal_id}`);
return response;
}
}
module.exports = MangaAPI;
Query -
query getMangaDetail{
manga(id: 13){
related{
Adaptation{
name
}
AlternativeVersion{
name
}
SpinOff{
name
}
}
}
}
Getting null in those fields which are having space and hyphen.
Query result -
{
"data": {
"manga": {
"related": {
"Adaptation": [
{
"name": "One Piece"
}
],
"AlternativeVersion": null,
"SpinOff": null
}
}
}
}
Repository - jikan-graphql
According to the spec, names in GraphQL must follow this format:
/[_A-Za-z][_0-9A-Za-z]*/
In other words, neither spaces nor dashes are permitted. If your data source is returning property names that are formatted incorrectly, you can just provide resolvers for the fields in question:
const resolvers = {
RelatedType: {
sideStory: (parent) => {
return parent['Side story']
},
...
}
}

Running a graphql query from inside a resolver - for nested data

I am implementing an apollo server graphql schema. All my schema definition are modules in .graphql files. All my resolvers are modules in .js files.
I have the following type :
productSchema.graphql
type Product {
_id: Int
company: Company
productSellingPrice: [PriceHistoryLog]
productName: String
category: String
productDetails: [ProductDetail]
globalId: Int
isActive: Boolean
}
extend type Query {
ProductList: [Product]
}
productDetailSchema.graphql
type ProductDetail {
_id: Int
company: Company
root: Product
catalogItem: CatalogItem
product: Product
isPerishable: Boolean
quantity: Float
isActive: Boolean
}
extend type Query {
ProductDetailsList(productId: Int!): [ProductDetail]
}
What I want to do is, when querying for ProductList, to run a ProductDetailsList query and resolve the field in product from there.
As you can see ProductDetail also have nested fields so I can't just query the DB for that field in the Product resolver.
Any ideas? I am kind of lost.
Edit:
this is my resolver code:
Product: {
company: product => product.companyId,
category: async product => {
try {
let res = await SaleModel.findOne({ productName:
product.productName }) ;
return res.productCategory;
} catch (err) {
console.log(err);
return "Mo Category found";
}
}
},
Query: {
async ProductList(obj, args, { companyId }) {
return await ProductModel.find({
companyId,
isActive: true
}).populate("companyId");
}
},

Write resolvers for nested type definitions

Suppose I have following type definition for my GraphQL API:
const typeDef = `
type Book {
title: String
author: Author
likes: Int
}
type Author {
id: String
name: String
age: Int
books: [Book]
}
type Query{
books(authorid: String!): Book
}
`
Then, how many resolvers do I need for this? Should I handle this query request with only one resolver books and return all books and author info or should I make many resolvers such as Query -> books, Book -> author and Author -> books? I am not sure how the modular schema and resolver works together.
No matter how many type(Book, Author etc) or input you use you need to provide .
const schema = `
type Mutation {
mutatePost(postId:Int) :Int
}
type Query {
hello: String
posts: [String]
books(authorId: String!): Book
}
`
You need to use same name as you defined in Query must be same in resolver
const resolvers = {
Query: {
async hello() {
return 'Hello';
},
async posts() {
return ['Hello', 'World];
},
async books(_, { authorId }) {
//Return data which you is defined in type Book
//return Book
}
},
Mutation: {
async mutatePost(_, {
postId
}, context) {
//return Integer
}
},
}
Only thing every Query and Mutation need queryResolver and mutationResolver

How to write graphql query wiith custom objects

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

Resources