Spring Boot GraphQL: Expected type to be a GraphQLInputType, but it wasn't - spring

I am trying to set up a GraphQL endpoint in Spring Boot, and when I try to run my App, I get the following error:
Expected type 'Order' to be a GraphQLInputType, but it wasn't! Was a type only permitted for object types incorrectly used as an input type, or vice-versa?
Here is my models.graphqls:
type Order {
id: String!
storeOrderId: String
connectionId: String
}
type Mutation {
createOrder(order: Order): Order
}

This is happening because you're trying to use a type for an input, which isn't allowed.
type Order {
Should be...
input Order {
So that the whole thing looks like:
I am trying to set up a GraphQL endpoint in Spring Boot, and when I try to run my App, I get the following error:
Expected type 'Order' to be a GraphQLInputType, but it wasn't! Was a type only permitted for object types incorrectly used as an input type, or vice-versa?
Here is my models.graphqls:
input Order {
id: String!
storeOrderId: String
connectionId: String
}
type Mutation {
createOrder(order: Order): Order
}

if it still does't work after use a input type, There may be other problems here.
the input java types should have public getters,
the input java types should also implement Serializable interface

Related

How to define an arbitrary scalar in apollo gql?

I have a type that needs to be like the following
type ActivityPayload {
action: String!
extra: AnythingAtAll
}
Where AnythingAtAll is an arbitrary JSON format. So that's as far as I get because all the tutorials I see expect you to have a type AnythingAtAll with fields defined inside of it. How do I allow {}, or {any properties in json format no matter what the property names and values are}
Use graphql-scalars:
A library of custom GraphQL scalar types for creating precise type-safe GraphQL schemas.
This library offers also the scalar type JSON:
The JSON scalar type represents JSON values as specified by ECMA-404.
Then you can do the following:
type ActivityPayload {
action: String!
extra: JSON
}
See also graphql-type-json (JSON is based on this one).

can't use type 'object' for Type-GraphQL field types

I would like to create a GraphQL layer for my NestJs API. So based on this interface holding a key/value pairs
export interface Mapping {
id: string;
value: object;
}
I created a DTO acting as a return type for the GraphQL queries
#ObjectType()
export class MappingDTO {
#Field(() => ID)
id: string;
#Field()
value: object;
}
So when I want to find all mappings I would come up with this
#Query(() => [MappingDTO])
public async mappings(): Promise<Mapping[]> {
return this.mappingsService.getMappings();
}
Unfortunately I can't use object for the value field. I'm getting this error
NoExplicitTypeError: You need to provide explicit type for
MappingDTO#value !
When chaning the type e.g. to string no error gets thrown on startup. The thing is that I can't specify an explicit type because my MongoDB stores objects holding "anything".
How can I fix my resolver or DTO to deal with an object type?
Edit:
Same for the resolver decorator. When I have this
#Query(() => Object)
public async getValueByKey(#Args('id') id: string): Promise<object> {
return this.mappingsService.getValueByKey(id);
}
I get this error
Error: Cannot determine GraphQL output type for getValueByKey
Sounds like you should be using a custom JSON scalar. Like this one.
import { GraphQLJSONObject } from 'graphql-type-json'
This can then either be used directly in the field decorator:
#Field(() => GraphQLJSONObject)
value: object
or you can leave the decorator as #Field() and add the appropriate scalarsMap to the options you pass to buildSchema. See the docs for additional details.
Just to point out the fact that the whole idea of GraphQL is declarative data fetching.
Also you can define custom types in GraphQL the scalar types don't have object as option, but you can define a type of yourself like this.
type customObject {
keyOne: Int!
KeyTwo: String!
...
key(n): ...
}
and use this type in Interfaces, Query, Mutations:
export interface Mapping {
id: string;
value: customObject;
}
You might think that you will have to define every key of the object you are storing in the mongoDB in this type customObject, but you don't have to. this is where the GraphQL declarative data fetching comes in. You only define those fields in the type (customObject) that you want to use on the front end, you don't need to fetch the complete object from DB.

Exception using nested Input Types in GraphQL and Spring Boot

I'm trying to create a Mutation that receives an input type like this:
input Cart {
items: [CartItem!]!
client: String!
}
input CartItem {
productId: Int!
qty: Int!
}
And my Mutation goes like this:
type Mutation {
createOrder(cart: Cart!): Order!
}
But when I try to run my Application I get the following exception:
Caused by: com.coxautodev.graphql.tools.SchemaError: Expected type 'CartItem' to be a GraphQLOutputType, but it wasn't! Was a type only permitted for object types incorrectly used as an input type, or vice-versa?
What's going on?
It is a known bug of Graphql-java-tools: https://github.com/graphql-java-kickstart/graphql-java-tools/issues/216
The input java types should have public getters.
You should initiate a proper class implementation for their respective graphql schema declaration.
You can check an example here: https://github.com/MerNat/graphql-spring-boot-nested-input-types-example
except the input java types should have public getters, the input java types should also implement Serializable interface

How to require propertyA OR propertyB in a GraphQL Schema

In the type definition below, is there a way to require name or model, instead of name and model?
type Starship {
id: ID!
name: String!
model: String!
length(unit: LengthUnit = METER): Float
}
I may have name or model due to some legacy data limitations. I would rather enforce this at the GraphQL validation layer, rather than in code.
EDIT:
There is some good discussion about adding validation to the graphQL spec, which you can read here: https://github.com/graphql/graphql-js/issues/361
There are also a couple of libraries to extend validation:
https://github.com/xpepermint/graphql-type-factory
https://github.com/stephenhandley/graphql-validated-types
I'm going to stick with validating the types in code, at least until they add better support.
You could try to use union to represent name or model concept . As union only works with object type now , that means you have also model name and model as object type first.
Code wise the schema looks like :
type Name {
value : String!
}
type Model {
value : String!
}
union NameOrModel = Name | Model
type Starship {
id: ID!
nameOrModel : NameOrModel!
length(unit: LengthUnit = METER): Float
}
It is very ugly IMO as it introduces many unnecessary noise and complexity to the schema .So I would prefer to stick with your original schema and do that check manually in the backend.
From the spec:
By default, all types in GraphQL are nullable; the null value is a valid response for all of the above types. To declare a type that disallows null, the GraphQL Non‐Null type can be used. This type wraps an underlying type, and this type acts identically to that wrapped type, with the exception that null is not a valid response for the wrapping type. A trailing exclamation mark is used to denote a field that uses a Non‐Null type like this: name: String!.
An individual field may be nullable or non-nullable. Non-null validation happens at the field level, independent of other fields. So there is no mechanism for validating whether some combination of fields are or are not null.

AWS AppSync GraphQL input validation - ignore extra fields?

I have an input type in my schema that specifies lots of attributes, as it's intended to do. The issue is that what I'm sending to the mutation that will persist these objects is an object with arbitrary fields that may change. As it stands, if I send attributes not specified in the schema, I get the error:
Validation error of type WrongType: argument 'input' with value (...)
contains a field not in 'BotInput': 'ext_gps' # 'setBot'
Concretely, my input type did not specify the attribute exp_gps, and that field was provided.
My Question
Is there a way to make it so the input validation simply ignores any attributes not in the schema, so that it continues to perform the mutation with only whatever was specified in the schema? It'll be often that I don't want to persist the additional attributes, so dropping them is fine, as long as the other attributes get added.
GraphQL does not support arbitrary fields, there is a RFC to support a Map type but it has not been merged/approved into the specification.
I see two possible workarounds that both require to change your schema a little bit.
Say you have the following schema:
type Mutation {
saveBot(input: BotInput) : Boolean
}
input BotInput {
id: ID!
title: String
}
and the input object is:
{
"id": "123",
"title": "GoogleBot",
"unrelated": "field",
"ext_gps": "else"
}
Option 1: Pass the arbitrary fields as AWSJSON
You would change your schema to:
type Mutation {
saveBot(input: BotInput) : Boolean
}
input BotInput {
id: ID!
title: String
arbitraryFields: AWSJSON // this will contain all the arbitrary fields in a json string, provided your clients can pluck them from the original object, make a map out of them and json serialize it.
}
So the input in our example would be now:
{
"id": "123",
"title": "GoogleBot",
"arbitraryFields": "{\"unrelated\": \"field\", \"ext_gps\": \"else\"}"
}
In your resolver, you could take the arbitraryFields string, deserialize it, and hydrate the values on the BotInput object before passing it to the data source.
Option 2: Pass the input as AWSJSON
The principle is the same but you pass the entire BotInput as AWSJSON.
type Mutation {
saveBot(input: AWSJSON) : Boolean
}
You don't have to do the resolver hydration and you don't have to change your client, but you lose the GraphQL type validation as the whole BotInput is now a blob.

Resources