Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure Jackson codec specifically for GraphQL HTTP endpoints #860

Closed
martinformi opened this issue Nov 22, 2023 · 9 comments
Closed

Configure Jackson codec specifically for GraphQL HTTP endpoints #860

martinformi opened this issue Nov 22, 2023 · 9 comments
Assignees
Labels
in: web Issues related to web handling type: enhancement A general enhancement
Milestone

Comments

@martinformi
Copy link

martinformi commented Nov 22, 2023

I tried to open the issue here but I was forwarded back to spring boot.

Version: Spring Boot 3.1.5

  • Kotlin, WebFlux

Current Behavior

I am using graphiql with spring-boot-graphql-starter, however I am getting an error even with the simple schema. Can you please verify what is wrong with the response from introspection? Thank you.

Schema:

type Query {
    getAll: String
}

Error:

{
  "errors": [
    {
      "message": "Introspection result missing interfaces: { kind: \"OBJECT\", name: \"Deployment\", fields: [[Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], ... 5 more items] }.",
      "stack": "Error: Introspection result missing interfaces: { kind: \"OBJECT\", name: \"Deployment\", fields: [[Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], ... 5 more items] }.\n    at buildImplementationsList (https://unpkg.com/graphiql/graphiql.min.js:33298:13)\n    at interfaces (https://unpkg.com/graphiql/graphiql.min.js:33306:25)\n    at resolveReadonlyArrayThunk (https://unpkg.com/graphiql/graphiql.min.js:29728:40)\n    at defineInterfaces (https://unpkg.com/graphiql/graphiql.min.js:29908:22)\n    at GraphQLObjectType._interfaces (https://unpkg.com/graphiql/graphiql.min.js:29868:30)\n    at GraphQLObjectType.getInterfaces (https://unpkg.com/graphiql/graphiql.min.js:29882:31)\n    at collectReferencedTypes (https://unpkg.com/graphiql/graphiql.min.js:32130:45)\n    at new GraphQLSchema (https://unpkg.com/graphiql/graphiql.min.js:31973:9)\n    at Object.buildClientSchema (https://unpkg.com/graphiql/graphiql.min.js:33212:10)\n    at https://unpkg.com/graphiql/graphiql.min.js:64435:23"
    }
  ]
}

Response from call for instrospection:

Response Details
{
    "data": {
        "__schema": {
            "queryType": {
                "name": "Query"
            },
            "types": [
                {
                    "kind": "SCALAR",
                    "name": "Boolean",
                    "description": "Built-in Boolean"
                },
                {
                    "kind": "OBJECT",
                    "name": "Query",
                    "fields": [
                        {
                            "name": "getAll",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "SCALAR",
                    "name": "String",
                    "description": "Built-in String"
                },
                {
                    "kind": "OBJECT",
                    "name": "__Directive",
                    "fields": [
                        {
                            "name": "name",
                            "description": "The __Directive type represents a Directive that a server supports.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "String"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "isRepeatable",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "Boolean"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "locations",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "LIST",
                                    "ofType": {
                                        "kind": "NON_NULL",
                                        "ofType": {
                                            "kind": "ENUM",
                                            "name": "__DirectiveLocation"
                                        }
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "args",
                            "args": [
                                {
                                    "name": "includeDeprecated",
                                    "type": {
                                        "kind": "SCALAR",
                                        "name": "Boolean"
                                    },
                                    "defaultValue": "false"
                                }
                            ],
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "LIST",
                                    "ofType": {
                                        "kind": "NON_NULL",
                                        "ofType": {
                                            "kind": "OBJECT",
                                            "name": "__InputValue"
                                        }
                                    }
                                }
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "ENUM",
                    "name": "__DirectiveLocation",
                    "description": "An enum describing valid locations where a directive can be placed",
                    "enumValues": [
                        {
                            "name": "QUERY",
                            "description": "Indicates the directive is valid on queries.",
                            "isDeprecated": false
                        },
                        {
                            "name": "MUTATION",
                            "description": "Indicates the directive is valid on mutations.",
                            "isDeprecated": false
                        },
                        {
                            "name": "SUBSCRIPTION",
                            "description": "Indicates the directive is valid on subscriptions.",
                            "isDeprecated": false
                        },
                        {
                            "name": "FIELD",
                            "description": "Indicates the directive is valid on fields.",
                            "isDeprecated": false
                        },
                        {
                            "name": "FRAGMENT_DEFINITION",
                            "description": "Indicates the directive is valid on fragment definitions.",
                            "isDeprecated": false
                        },
                        {
                            "name": "FRAGMENT_SPREAD",
                            "description": "Indicates the directive is valid on fragment spreads.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INLINE_FRAGMENT",
                            "description": "Indicates the directive is valid on inline fragments.",
                            "isDeprecated": false
                        },
                        {
                            "name": "VARIABLE_DEFINITION",
                            "description": "Indicates the directive is valid on variable definitions.",
                            "isDeprecated": false
                        },
                        {
                            "name": "SCHEMA",
                            "description": "Indicates the directive is valid on a schema SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "SCALAR",
                            "description": "Indicates the directive is valid on a scalar SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "OBJECT",
                            "description": "Indicates the directive is valid on an object SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "FIELD_DEFINITION",
                            "description": "Indicates the directive is valid on a field SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "ARGUMENT_DEFINITION",
                            "description": "Indicates the directive is valid on a field argument SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INTERFACE",
                            "description": "Indicates the directive is valid on an interface SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "UNION",
                            "description": "Indicates the directive is valid on an union SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "ENUM",
                            "description": "Indicates the directive is valid on an enum SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "ENUM_VALUE",
                            "description": "Indicates the directive is valid on an enum value SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INPUT_OBJECT",
                            "description": "Indicates the directive is valid on an input object SDL definition.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INPUT_FIELD_DEFINITION",
                            "description": "Indicates the directive is valid on an input object field SDL definition.",
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "OBJECT",
                    "name": "__EnumValue",
                    "fields": [
                        {
                            "name": "name",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "String"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "isDeprecated",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "Boolean"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "deprecationReason",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "OBJECT",
                    "name": "__Field",
                    "fields": [
                        {
                            "name": "name",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "String"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "args",
                            "args": [
                                {
                                    "name": "includeDeprecated",
                                    "type": {
                                        "kind": "SCALAR",
                                        "name": "Boolean"
                                    },
                                    "defaultValue": "false"
                                }
                            ],
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "LIST",
                                    "ofType": {
                                        "kind": "NON_NULL",
                                        "ofType": {
                                            "kind": "OBJECT",
                                            "name": "__InputValue"
                                        }
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "type",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "OBJECT",
                                    "name": "__Type"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "isDeprecated",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "Boolean"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "deprecationReason",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "OBJECT",
                    "name": "__InputValue",
                    "fields": [
                        {
                            "name": "name",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "String"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "type",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "OBJECT",
                                    "name": "__Type"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "defaultValue",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "isDeprecated",
                            "type": {
                                "kind": "SCALAR",
                                "name": "Boolean"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "deprecationReason",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "OBJECT",
                    "name": "__Schema",
                    "description": "A GraphQL Introspection defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, the entry points for query, mutation, and subscription operations.",
                    "fields": [
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "types",
                            "description": "A list of all types supported by this server.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "LIST",
                                    "ofType": {
                                        "kind": "NON_NULL",
                                        "ofType": {
                                            "kind": "OBJECT",
                                            "name": "__Type"
                                        }
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "queryType",
                            "description": "The type that query operations will be rooted at.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "OBJECT",
                                    "name": "__Type"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "mutationType",
                            "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
                            "type": {
                                "kind": "OBJECT",
                                "name": "__Type"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "directives",
                            "description": "'A list of all directives supported by this server.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "LIST",
                                    "ofType": {
                                        "kind": "NON_NULL",
                                        "ofType": {
                                            "kind": "OBJECT",
                                            "name": "__Directive"
                                        }
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "subscriptionType",
                            "description": "'If this server support subscription, the type that subscription operations will be rooted at.",
                            "type": {
                                "kind": "OBJECT",
                                "name": "__Type"
                            },
                            "isDeprecated": false
                        }
                    ]
                },
                {
                    "kind": "OBJECT",
                    "name": "__Type",
                    "fields": [
                        {
                            "name": "kind",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "ENUM",
                                    "name": "__TypeKind"
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "name",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "description",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "fields",
                            "args": [
                                {
                                    "name": "includeDeprecated",
                                    "type": {
                                        "kind": "SCALAR",
                                        "name": "Boolean"
                                    },
                                    "defaultValue": "false"
                                }
                            ],
                            "type": {
                                "kind": "LIST",
                                "ofType": {
                                    "kind": "NON_NULL",
                                    "ofType": {
                                        "kind": "OBJECT",
                                        "name": "__Field"
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "interfaces",
                            "type": {
                                "kind": "LIST",
                                "ofType": {
                                    "kind": "NON_NULL",
                                    "ofType": {
                                        "kind": "OBJECT",
                                        "name": "__Type"
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "possibleTypes",
                            "type": {
                                "kind": "LIST",
                                "ofType": {
                                    "kind": "NON_NULL",
                                    "ofType": {
                                        "kind": "OBJECT",
                                        "name": "__Type"
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "enumValues",
                            "args": [
                                {
                                    "name": "includeDeprecated",
                                    "type": {
                                        "kind": "SCALAR",
                                        "name": "Boolean"
                                    },
                                    "defaultValue": "false"
                                }
                            ],
                            "type": {
                                "kind": "LIST",
                                "ofType": {
                                    "kind": "NON_NULL",
                                    "ofType": {
                                        "kind": "OBJECT",
                                        "name": "__EnumValue"
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "inputFields",
                            "args": [
                                {
                                    "name": "includeDeprecated",
                                    "type": {
                                        "kind": "SCALAR",
                                        "name": "Boolean"
                                    },
                                    "defaultValue": "false"
                                }
                            ],
                            "type": {
                                "kind": "LIST",
                                "ofType": {
                                    "kind": "NON_NULL",
                                    "ofType": {
                                        "kind": "OBJECT",
                                        "name": "__InputValue"
                                    }
                                }
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "ofType",
                            "type": {
                                "kind": "OBJECT",
                                "name": "__Type"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "specifiedByURL",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": false
                        },
                        {
                            "name": "specifiedByUrl",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "isDeprecated": true,
                            "deprecationReason": "This legacy name has been replaced by `specifiedByURL`"
                        }
                    ]
                },
                {
                    "kind": "ENUM",
                    "name": "__TypeKind",
                    "description": "An enum describing what kind of type a given __Type is",
                    "enumValues": [
                        {
                            "name": "SCALAR",
                            "description": "Indicates this type is a scalar. 'specifiedByURL' is a valid field",
                            "isDeprecated": false
                        },
                        {
                            "name": "OBJECT",
                            "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INTERFACE",
                            "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
                            "isDeprecated": false
                        },
                        {
                            "name": "UNION",
                            "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
                            "isDeprecated": false
                        },
                        {
                            "name": "ENUM",
                            "description": "Indicates this type is an enum. `enumValues` is a valid field.",
                            "isDeprecated": false
                        },
                        {
                            "name": "INPUT_OBJECT",
                            "description": "Indicates this type is an input object. `inputFields` is a valid field.",
                            "isDeprecated": false
                        },
                        {
                            "name": "LIST",
                            "description": "Indicates this type is a list. `ofType` is a valid field.",
                            "isDeprecated": false
                        },
                        {
                            "name": "NON_NULL",
                            "description": "Indicates this type is a non-null. `ofType` is a valid field.",
                            "isDeprecated": false
                        }
                    ]
                }
            ],
            "directives": [
                {
                    "name": "include",
                    "description": "Directs the executor to include this field or fragment only when the `if` argument is true",
                    "locations": [
                        "FIELD",
                        "FRAGMENT_SPREAD",
                        "INLINE_FRAGMENT"
                    ],
                    "args": [
                        {
                            "name": "if",
                            "description": "Included when true.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "Boolean"
                                }
                            }
                        }
                    ]
                },
                {
                    "name": "skip",
                    "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
                    "locations": [
                        "FIELD",
                        "FRAGMENT_SPREAD",
                        "INLINE_FRAGMENT"
                    ],
                    "args": [
                        {
                            "name": "if",
                            "description": "Skipped when true.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "Boolean"
                                }
                            }
                        }
                    ]
                },
                {
                    "name": "deprecated",
                    "description": "Marks the field, argument, input field or enum value as deprecated",
                    "locations": [
                        "FIELD_DEFINITION",
                        "ARGUMENT_DEFINITION",
                        "ENUM_VALUE",
                        "INPUT_FIELD_DEFINITION"
                    ],
                    "args": [
                        {
                            "name": "reason",
                            "description": "The reason for the deprecation",
                            "type": {
                                "kind": "SCALAR",
                                "name": "String"
                            },
                            "defaultValue": "\"No longer supported\""
                        }
                    ]
                },
                {
                    "name": "specifiedBy",
                    "description": "Exposes a URL that specifies the behaviour of this scalar.",
                    "locations": [
                        "SCALAR"
                    ],
                    "args": [
                        {
                            "name": "url",
                            "description": "The URL that specifies the behaviour of this scalar.",
                            "type": {
                                "kind": "NON_NULL",
                                "ofType": {
                                    "kind": "SCALAR",
                                    "name": "String"
                                }
                            }
                        }
                    ]
                }
            ]
        }
    }
}

Expected Behavior

Schema explorer should work

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 22, 2023
@martinformi
Copy link
Author

martinformi commented Nov 22, 2023

After investigation I compared two responses:

  1. Working implementation on Java non-webflux service - Spring Boot 3.1.5
  2. Not working implementation on Kotlin Webflux service - Spring Boot 2.7.8

In the not working implementation interfaces: [] and args: [] is missing in introspection response:

image

This is related to the problem, that Jackson is not serializing empty lists. The question is, how can I configure the object mapper for the GraphQL?

@bclozel bclozel transferred this issue from spring-projects/spring-boot Nov 22, 2023
@bclozel bclozel changed the title [spring-boot-graphql][graphiql] Introspection result missing interfaces Configure Jackson codec specifically for GraphQL endpoints Nov 22, 2023
@bclozel
Copy link
Member

bclozel commented Nov 22, 2023

I think this is the expected behavior so far in Spring Boot and Spring for GraphQL - the same Jackson codec/message converter is used for all web endpoints. In Spring Boot, changing spring.jackson.* configuration properties will have an effect there. Did you apply such a configuration?

There would be one way to configure a Jackson Decoder dedicated to a particular media type, here "application/graphql-response+json". But the same would not be applied to "application/json" responses if graphQL client request so.

I'll leave this opened for now so that we can discuss this within the team.

@rstoyanchev
Copy link
Contributor

We've discussed this further internally.

Currently, HTTP handlers rely on the web framework to (de)serialize to/from maps of data, so it shares the setup with any web endpoint. To make it independent, we would need to change them to take an HttpMessageConverter (WebMvc) or CodecConfigurer (WebFlux) and do that work internally. This is what the WebSocket handlers already do. Then the Boot autoconfig would have to provide some way of deviating from the main config for message conversion.

@rstoyanchev rstoyanchev added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 8, 2023
@rstoyanchev rstoyanchev added this to the 1.3 Backlog milestone Dec 8, 2023
@rstoyanchev rstoyanchev modified the milestones: 1.3 Backlog, 1.3.0-M1 Jan 25, 2024
@bclozel bclozel modified the milestones: 1.3.0-M1, 1.3.0-M2 Feb 17, 2024
@bclozel bclozel added the in: web Issues related to web handling label Feb 17, 2024
@acao
Copy link

acao commented Feb 20, 2024

On a high graphql protocol level because I have no context of spring boot but I want our web tooling and LSP server to work for sprint boot graphql users, are you saying that the "application/graphql-response+json" content type used in the introspection request's Accept header would return the necessary interfaces for a complete schema? and application/json returns a limited schema?

Edit: Upon re-reading the http spec, this is completely missing! We should make this content type the default for graphiql.

I wonder @rstoyanchev now that I understand the issue better (graphiql being out of date with the http spec 😆 ), if this is indeed the bug, what happens if you add to createGraphiQLFetcher the header Accept: application/graphql-repsonse-json ? perhaps spring graphql related graphiql implementations can adopt this if they haven't already until I fix this on the GraphiQL end

@acao
Copy link

acao commented Feb 20, 2024

Again, I have 0 context on this probably incredible framework, but from my understanding, if this is about adopting the new spec compliant content type for introspection, then this is a bug entirely on GraphiQL's end (still sending the old application/json content type), and @rstoyanchev you can feel free to close the ticket if @bclozel or someone can confirm GraphiQL is the problem, as I'm re-opening this ticket on our end

@bclozel
Copy link
Member

bclozel commented Feb 21, 2024

Hello @acao !
This is not a GraphiQL issue at all, so no need to address anything on your side. Spring Boot allows to customize the JSON serialization process and sometimes this can make the introspection query result invalid. We will fix that by isolating more the JSON processing for graphQL on our side.

As for the adoption of the new content type, it is really up to you to consider the draft spec, its timeline and your user base.

@bclozel bclozel changed the title Configure Jackson codec specifically for GraphQL endpoints Configure Jackson codec specifically for GraphQL HTTP endpoints Apr 2, 2024
@bclozel bclozel closed this as completed in 9e4d773 Apr 2, 2024
@bclozel
Copy link
Member

bclozel commented Apr 2, 2024

This is now fixed in 1.3.0-SNAPSHOTs. Applications can configure GraphQlHttpHandler beans with custom codecs.

Here is an example of how to configure a vanilla ObjectMapper in Spring Boot for Spring for GraphQL with Spring MVC:

    @Bean
    public GraphQlHttpHandler graphQlHttpHandler(WebGraphQlHandler graphQlHandler) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(new ObjectMapper());
        return new GraphQlHttpHandler(graphQlHandler, converter);
    }

And now with WebFlux:

    @Bean
    public GraphQlHttpHandler graphQlHttpHandler(WebGraphQlHandler webGraphQlHandler) {
        ObjectMapper mapper = new ObjectMapper();
        CodecConfigurer configurer = ServerCodecConfigurer.create();
        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper));
        configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper));
        return new GraphQlHttpHandler(webGraphQlHandler, configurer);
    }

@martinformi
Copy link
Author

Thank you for the update.

@bclozel
Copy link
Member

bclozel commented Apr 2, 2024

@acao from our perspective this is now solved. I'm not sure if there's anything to do in GraphiQL or graphql-js. In this case, the server is responding without the "interfaces": [] and "args": [] parts because the JSON codec is probably configured to ignore properties if they have empty values.

If those bits are always required by the spec, then this is only an issue on our end. If the spec allows those properties to be missing, then maybe graphql-js could be more lenient about that. I'm not sure where this information can be found - in any case I'll defer to you.

Thanks for maintaining graphiql!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues related to web handling type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants