Exception using nested Input Types in GraphQL and Spring Boot - 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

Related

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

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

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.

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.

How to use the query for custom fields on GraphQL?

I have this schema on my graphcool:
type User #model {
id: ID! #isUnique
name: String!
email: String!
password: String!
}
Using playground, I can execute this properly:
query {
User(id: "1234") {
id
name
}
}
But this query:
query {
User(name: "Thomas") {
id
name
}
}
throws error:
Unknown argument 'name' on field 'User' of type 'Query'. (line 2,
column 8):
User(name: "Thomas").
Why? And how to fix this? From my pov, anything that's already on the model, can be queried immediately, right? Btw, I'm very newbie in graphQL, and there's almost no article talk about this error (every tutorial just like assume that this will immediately works), so please give some more elaborate answer if necessary.
GraphQL does not intrinsically allow arbitrary queries against objects.
Somewhere in your schema there will be an additional declaration like
type Query {
User(id: ID!): User
}
The names in the Query type are the top-level queries you can run, and the arguments listed in that query are the only arguments they accept. (There is a corresponding Mutation type for top-level mutations, which can change the underlying state, and use the mutation keyword in a query.)
If you control the server implementation, you could add a parameter or an additional top-level query
userByName(name: String!): User
but you'd also have to provide an implementation of this query or handle the additional parameter, which is a code change.

How to deal with nested input in GraphQL

When writing queries I can define a resolver on any field and that field’s value will be determined by its resolver, regardless of query depth.
However when writing a mutation I seem to only be able to define the resolvers at the root level. Adding a resolve method to fields in my args or input type does not seem to have any affect.
What’s the best way deal with nested input in mutations?
What do you mean by nested input in your mutations? GraphQL input types does not have resolvers. With resolvers you are just determining how to fetch results. If you would like to have nested input, e.g. for example I would like to create user also with company. I will then define CreateUserInput CreateCompanyInput type for example like this in SDL
input CreateCompanyInput {
name: String!
type: CompanyEnum!
}
input CreateUserInput {
username: String!
firstname: String!
lastname: String!
company: CreateCompanyInput!
}
type Mutation {
createUser(input: CreateUserInput!): User
}
This way I am basically nesting arguments and can implement more complex mutations. In addition I can reuse the CreateCompanyInput for createCompany mutation if I need mutation even for that. I will then have the whole CreateUserInput even with CreateCompanyInput in the createUser resolver as input argument. I can apply transactions as I will create two new records etc. Not sure if it is what you mean by nested input if you mean something else. Just let me know :)

Resources