{"id":15192437,"url":"https://github.com/leangen/graphql-spqr-spring-boot-starter","last_synced_at":"2025-05-16T15:06:40.350Z","repository":{"id":45126869,"uuid":"129529718","full_name":"leangen/graphql-spqr-spring-boot-starter","owner":"leangen","description":"Spring Boot 2 starter powered by GraphQL SPQR","archived":false,"fork":false,"pushed_at":"2024-08-14T10:14:55.000Z","size":267,"stargazers_count":279,"open_issues_count":72,"forks_count":68,"subscribers_count":32,"default_branch":"master","last_synced_at":"2025-04-12T12:54:46.434Z","etag":null,"topics":["graphql","graphql-api","graphql-server","graphql-spqr","spring-boot-2","spring-boot-starter"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leangen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-14T15:29:57.000Z","updated_at":"2025-03-30T00:25:45.000Z","dependencies_parsed_at":"2024-11-29T06:04:27.770Z","dependency_job_id":null,"html_url":"https://github.com/leangen/graphql-spqr-spring-boot-starter","commit_stats":{"total_commits":137,"total_committers":15,"mean_commits":9.133333333333333,"dds":"0.44525547445255476","last_synced_commit":"35471c050a33978aed244727f00fb0953ea09cd6"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leangen%2Fgraphql-spqr-spring-boot-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leangen%2Fgraphql-spqr-spring-boot-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leangen%2Fgraphql-spqr-spring-boot-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leangen%2Fgraphql-spqr-spring-boot-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leangen","download_url":"https://codeload.github.com/leangen/graphql-spqr-spring-boot-starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553959,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphql","graphql-api","graphql-server","graphql-spqr","spring-boot-2","spring-boot-starter"],"created_at":"2024-09-27T21:23:30.243Z","updated_at":"2025-05-16T15:06:40.333Z","avatar_url":"https://github.com/leangen.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# graphql-spqr-spring-boot-starter\n\u003e Spring Boot starter powered by [GraphQL SPQR](https://github.com/leangen/graphql-spqr)\n\n[![Join the chat at https://gitter.im/leangen/graphql-spqr](https://img.shields.io/gitter/room/leangen/graphql-spqr?color=green\u0026logo=gitter\u0026style=flat-square)](https://gitter.im/leangen/graphql-spqr?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![StackOverflow](https://img.shields.io/static/v1?label=stackoverflow\u0026message=graphql-spqr\u0026color=green\u0026style=flat-square)](https://stackoverflow.com/questions/tagged/graphql-spqr)\n[![Maven Central](https://img.shields.io/maven-central/v/io.leangen.graphql/graphql-spqr-spring-boot-starter?color=green\u0026style=flat-square)](https://maven-badges.herokuapp.com/maven-central/io.leangen.graphql/spqr)\n[![Javadoc](https://img.shields.io/badge/dynamic/json.svg?style=flat-square\u0026prefix=v\u0026color=green\u0026label=javadoc\u0026query=$.response.docs[0].latestVersion\u0026uri=http%3A%2F%2Fsearch.maven.org%2Fsolrsearch%2Fselect%3Fq%3Dg%3A%2522io.leangen.graphql%2522%2BAND%2Ba%3A%2522graphql-spqr-spring-boot-starter%2522%26wt%3Djson)](http://www.javadoc.io/doc/io.leangen.graphql/spqr)\n[![Build Status](https://img.shields.io/travis/leangen/graphql-spqr-spring-boot-starter?style=flat-square)](https://travis-ci.org/leangen/graphql-spqr)\n[![License](https://img.shields.io/github/license/leangen/graphql-spqr.svg?style=flat-square)](https://raw.githubusercontent.com/leangen/graphql-spqr/master/LICENSE)\n\n## Intro\n\nGraphQL SPQR Spring Boot starter aims to make it dead simple to add a GraphQL API to any Spring Boot project.\n * Add `@GraphQLApi` to any Spring managed component, and you're good to go 🚀\n * GraphQL endpoint available at `/graphql` by default\n * GraphQL Playground IDE (if enabled, see the properties below) available at `/ide`\n * Fully customizable in seconds by providing simple beans (any SPQR SPI can be exposed as a bean)\n\n## Project setup / Dependencies\n\nTo use this starter in a typical Spring Boot project, add the following dependencies to your project:\n\n```xml\n\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eio.leangen.graphql\u003c/groupId\u003e\n    \u003cartifactId\u003egraphql-spqr-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n  \u003c/dependency\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-web\u003c/artifactId\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n[There's also a very basic sample project](https://github.com/leangen/graphql-spqr-samples/tree/master/spring-boot-starter-sample) \n\n## Defining the operation sources (the beans that get exposed via the API)\n\nAll beans in Spring's application context annotated with `@GraphqlApi` are considered to be operation sources (a concept similar to `Controller` beans in Spring MVC).\nThis annotation can be used in combination with `@Component/@Service/@Repository` or `@Bean` annotations, e.g.\n\n```java\n    @Component\n    @GraphQLApi\n    private class UserService {\n        //Query/mutation/subscription methods\n        ...\n    }\n```\nor\n```java\n    @Bean\n    @GraphQLApi\n    public userService() {\n        return new UserService(...);\n    }\n```\n\n## Choosing which methods get exposed through the API\n\nTo deduce which methods of each operation source class should be exposed as GraphQL queries/mutations/subscriptions,\nSPQR uses the concept of a `ResolverBuilder` (since each exposed method acts as a resolver function for a GraphQL operation).\nTo cover the basic approaches `SpqrAutoConfiguration` registers a bean for each of the three built-in `ResolverBuilder` implementations:\n\n* `AnnotatedResolverBuilder` - exposes only the methods annotated by `@GraphQLQuery`, `@GraphQLMutation` or `@GraphQLSubscription`\n* `PublicResolverBuilder` - exposes all `public` methods from the operations source class (methods returning `void` are considered mutations)\n* `BeanResolverBuilder` - exposes all getters as queries and setters as mutations (getters returning `Publisher\u003cT\u003e` are considered subscriptions)\n* `RecordResolverBuilder` - exposes all record component accessors as queries (accessors returning `Publisher\u003cT\u003e` are considered subscriptions)\n\nIt is also possible to implement custom resolver builders by implementing the `ResolverBuilder` interface.\n\nResolver builders can be declared both globally and on the operation source level. If not sticking to the defaults, \nit is generally safer to explicitly customize on the operation source level, unless the rules are absolutely uniform across all operation sources.\nCustomizing on both levels simultaneously will work but could prove tricky to control as your API grows.\n\nDefaults:\n- For *top-level beans*: only `AnnotatedResolverBuilder` is registered, \n- For *nested beans*: `AnnotatedResolverBuilder`, `BeanResolverBuilder` and `RecordResolverBuilder` (applied to records only) are registered\n\n### Customizing resolver builders globally\n\nTo change the default resolver builders globally, implement and register a bean of type `ExtensionProvider\u003cResolverBuilder\u003e`.\nA simplified example of this could be:\n\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, ResolverBuilder\u003e resolverBuilderExtensionProvider() {\n        return (config, current) -\u003e {\n            List\u003cResolverBuilder\u003e resolverBuilders = new ArrayList\u003c\u003e();\n\n            //add a custom subtype of PublicResolverBuilder that only exposes a method if it's called \"greeting\"\n            resolverBuilders.add(new PublicResolverBuilder() {\n                @Override\n                protected boolean isQuery(Method method) {\n                    return super.isQuery(method) \u0026\u0026 method.getName().equals(\"greeting\");\n                }\n            });\n            //add the default builder\n            resolverBuilders.add(new AnnotatedResolverBuilder());\n\n            return resolverBuilders;\n        };\n    }\n```\nThis would add two resolver builders that apply to _all_ operation sources.\nThe First one exposes all public methods named _greeting_. The second is the inbuilt `AnnotatedResolverBuilder` (that exposes only the explicitly annotated methods).\nA quicker way to achieve the same would be:\n\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, ResolverBuilder\u003e resolverBuilderExtensionProvider() {\n        //prepend the custom builder to the provided list of defaults\n        return (config, current) -\u003e current.prepend(new PublicResolverBuilder() {\n                @Override\n                protected boolean isQuery(Method method) {\n                    return super.isQuery(method) \u0026\u0026 method.getName().equals(\"greeting\");\n                }\n            });\n    };\n```\n\n### Customizing the resolver builders for a specific operation source\n\nTo attach a resolver builder to a specific source (bean), use the `@WithResolverBuilder` annotation on it.\nThis annotation also works both on the beans registered by `@Component/@Service/@Repository` or `@Bean` annotations.\n\nAs an example, we can expose the `greeting` query by using:\n\n```java\n    @Component\n    @GraphQLApi\n    @WithResolverBuilder(BeanResolverBuilder.class) //exposes all getters\n    private class MyOperationSource {\n        public String getGreeting(){\n            return \"Hello world !\";\n        }\n    }\n```\n\nor:\n\n```java\n    @Bean\n    @GraphQLApi\n    //No explicit resolver builders declared, so AnnotatedResolverBuilder is used\n    public MyOperationSource() {\n        @GraphQLQuery(name = \"greeting\")\n        public String getGreeting() {\n            return \"Hello world !\";\n        }\n    }\n``` \n\nIt is also entirely possible to use more than one resolver builder on the same operation source e.g.\n\n```java\n    @Component\n    @GraphQLApi\n    @WithResolverBuilder(BeanResolverBuilder.class)\n    @WithResolverBuilder(AnnotatedResolverBuilder.class)\n    private class MyOperationSource {\n        //Exposed by BeanResolverBuilder because it's a getter\n        public String getGreeting(){\n            return \"Hello world !\";\n        }\n\n        //Exposed by AnnotatedResolverBuilder because it's annotated\n        @GraphQLQuery\n        public String personalGreeting(String name){\n            return \"Hello \" + name + \" !\"; \n        }\n    }\n```\nThis way, both queries are exposed but in different ways. The same would work on a bean registered using the `@Bean` annotation.\n\n## Customize GraphQL type information\n\nSometimes it is useful to have an automated strategy for generating type names, descriptions and order of fields within the type.\nTo do this SPQR uses `TypeInfoGenerator` on a global level. When using this starter the most convenient way is to wire a single bean of that type in the application context.\n\n```java\n    @Bean\n    public TypeInfoGenerator testTypeInfoGenerator() {\n        return new TypeInfoGenerator() {\n            @Override\n            public String generateTypeName(AnnotatedType type, MessageBundle messageBundle) {\n                return nameGenerationMethodLocalized(type, messageBundle);\n            }\n\n            @Override\n            public String generateTypeDescription(AnnotatedType type, MessageBundle messageBundle) {\n                return descriptionGenerationMethodLocalized(type, messageBundle);\n            }\n\n            @Override\n            public String[] getFieldOrder(AnnotatedType type, MessageBundle messageBundle) {\n                return fieldOrderGenerationMethodLocalized(type, messageBundle);\n            }\n\n        };\n    }\n```\n\n## Advanced config\n\n### Available Properties\n\n| Property | Default Value |\n| ------ | ------ |\n| graphql.spqr.base-packages | n/a |\n| graphql.spqr.abstract-input-type-resolution | false |\n| graphql.spqr.relay.enabled | false |\n| graphql.spqr.relay.mutation-wrapper | n/a |\n| graphql.spqr.relay.mutation-wrapper-description | n/a |\n| graphql.spqr.relay.connection-check-relaxed | false |\n| graphql.spqr.relay.spring-data-compatible | false |\n| graphql.spqr.http.enabled | true |\n| graphql.spqr.http.endpoint | /graphql |\n| graphql.spqr.http.mvc.executor | async |\n| graphql.spqr.ws.enabled | true |\n| graphql.spqr.ws.endpoint | n/a |\n| graphql.spqr.ws.send-time-limit | 10000 |\n| graphql.spqr.ws.send-buffer-size-limit | 512 * 1024 |\n| graphql.spqr.ws.allowed-origins | * |\n| graphql.spqr.ws.keep-alive.enabled | false |\n| graphql.spqr.ws.keep-alive.interval-millis | 10000 |\n| graphql.spqr.gui.enabled | true |\n| graphql.spqr.gui.endpoint | /gui |\n| graphql.spqr.gui.target-endpoint | n/a |\n| graphql.spqr.gui.target-ws-endpoint | n/a |\n| graphql.spqr.gui.page-title | GraphQL Playground |\n\n### Customize mapping of GraphQL values to Java values\n\nObject in charge of doing this in SPQR is `ValueMapperFactory`. Again the simplest way to make use of this when using the starter is to wire a single bean of this type into the application context.\n\n```java\n    @Bean\n    public ValueMapperFactory testValueMapperFactory() {\n        return (abstractTypes, environment) -\u003e new ValueMapper() {\n            @Override\n            public \u003cT\u003e T fromInput(Object graphQLInput, Type sourceType, AnnotatedType outputType) {\n                return null;\n            }\n\n            @Override\n            public \u003cT\u003e T fromString(String json, AnnotatedType type) {\n                return null;\n            }\n\n            @Override\n            public String toString(Object output) {\n                return null;\n            }\n        };\n    }\n``` \nNOTE: SPQR comes with `JacksonValueMapper` and `GsonValueMapperFactory` so in reality this should be rarely needed as these are by far the most frequently used libraries in Java.\n\n### Customizing input and output converters\n\nAnalogous to the rest of the configuration, single beans should be wired into the context. As this is done in functional style in SPQR it is not possible to set chains of `InputConverter` and `OutputConverter`, but by passing a lambda that will manipulate the chains.\n\nExtension provider for input converters\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, InputConverter\u003e testInputConverterExtensionProvider() {\n        return (config, current) -\u003e current.prepend( //Insert before the defaults. Or return a new list to take full control.\n            new InputConverter() {\n                @Override\n                public Object convertInput(Object substitute, AnnotatedType type, GlobalEnvironment environment, ValueMapper valueMapper) {\n                    return ...;\n                }\n\n                @Override\n                public boolean supports(AnnotatedType type) {\n                    return ...;\n                }\n\n                @Override\n                public AnnotatedType getSubstituteType(AnnotatedType original) {\n                    return ...;\n                }\n            }\n        );\n    }\n```\n\nExtension provider for output converters\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, OutputConverter\u003e testOutputConverterExtensionProvider() {\n         //Insert a custom converter after the built-in IdAdapter (which is generally a safe position).\n         //Return a new list instead to take full control. \n        return (config, current) -\u003e current.insertAfter(IdAdapter.class,\n            new OutputConverter() {\n                @Override\n                public Object convertOutput(Object original, AnnotatedType type, ResolutionEnvironment resolutionEnvironment) {\n                    return ...;\n                }\n\n                @Override\n                public boolean supports(AnnotatedType type) {\n                    return ...;\n                }\n            }\n         );\n    }\n```\n### Custom type mapper for GraphQL output and input types\n\nAgain wire a single bean of type `ExtensionProvider\u003cGeneratorConfiguration, TypeMapper\u003e` into the application context to manipulate mapper chain.\n\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, TypeMapper\u003e customTypeMappers() {\n    //Insert a custom mapper after the built-in IdAdapter (which is generally a safe position)\n    return (config, current) -\u003e current.insertAfter(IdAdapter.class,\n            new TypeMapper() {\n                @Override\n                public GraphQLOutputType toGraphQLType(AnnotatedType javaType, OperationMapper operationMapper, Set\u003cClass\u003c? extends TypeMapper\u003e\u003e mappersToSkip, BuildContext buildContext) {\n                    return new GraphQLOutputType() {\n                        @Override\n                        public String getName() {\n                            return ...;\n                        }\n                    };\n                }\n\n                @Override\n                public GraphQLInputType toGraphQLInputType(AnnotatedType javaType, OperationMapper operationMapper, Set\u003cClass\u003c? extends TypeMapper\u003e\u003e mappersToSkip, BuildContext buildContext) {\n                    return new GraphQLInputType() {\n                        @Override\n                        public String getName() {\n                            return ...;\n                        }\n                    };\n                }\n\n                @Override\n                public boolean supports(AnnotatedType type) {\n                    return ...;\n                }\n            }\n        );\n    }\n\n```\n\n### Custom argument injector\n\nAlso has a functional API, utilised by wiring a single bean of type `ExtensionProvider\u003cGeneratorConfiguration, ArgumentInjector\u003e`.\n\n```java\n    @Bean\n    public ExtensionProvider\u003cGeneratorConfiguration, ArgumentInjector\u003e testArgumentInjectorExtensionProvider() {\n        return (config, current) -\u003e current.prepend(\n           new ArgumentInjector() {\n               @Override\n               public Object getArgumentValue(ArgumentInjectorParams params) {\n                   return ...;\n               }\n\n               @Override\n               public boolean supports(AnnotatedType type, Parameter parameter) {\n                   return ...;\n               }\n\n           }\n        );\n    }\n```\n\n### Custom input fields\n\nWiring a single bean of type `ExtensionProvider\u003cGraphQLSchemaGenerator.ExtendedConfiguration, InputFieldBuilder\u003e` will allow you to manipulate the input builder chain.\n\n```java\n    @Bean\n    public ExtensionProvider\u003cExtendedGeneratorConfiguration, InputFieldBuilder\u003e testInputFieldBuilder() {\n        return (config, current) -\u003e current.prepend( //Prepend your custom builder so it goes before the built-in ones\n                new InputFieldBuilder() {\n                    @Override\n                    public Set\u003cInputField\u003e getInputFields(InputFieldBuilderParams params) {\n                        return ...; //Build the input fields for the given type\n                    }\n\n                    @Override\n                    public boolean supports(AnnotatedType type) {\n                        return ...; //Does this builder support the given type?\n                    }\n                });\n    }\n```\n\nNOTE: In SPQR `InputFieldBuilder` is already implemented by `JacksonValueMapper` and `GsonValueMapper`.\n\n\n### More to follow soon ...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleangen%2Fgraphql-spqr-spring-boot-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleangen%2Fgraphql-spqr-spring-boot-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleangen%2Fgraphql-spqr-spring-boot-starter/lists"}