{"id":16813794,"url":"https://github.com/albertoimpl/spring-cloud-gateway-graphql","last_synced_at":"2025-03-23T15:31:27.146Z","repository":{"id":45431616,"uuid":"434260300","full_name":"Albertoimpl/spring-cloud-gateway-graphql","owner":"Albertoimpl","description":"Blog post describing how Spring Cloud Gateway can help with some of the problems GraphQL has","archived":false,"fork":false,"pushed_at":"2021-12-15T08:38:40.000Z","size":1031,"stargazers_count":27,"open_issues_count":0,"forks_count":8,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-18T21:52:24.645Z","etag":null,"topics":["graphql","java","spring","spring-cloud-gateway"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Albertoimpl.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-02T14:48:26.000Z","updated_at":"2024-11-27T07:22:14.000Z","dependencies_parsed_at":"2022-08-19T06:22:09.829Z","dependency_job_id":null,"html_url":"https://github.com/Albertoimpl/spring-cloud-gateway-graphql","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Albertoimpl%2Fspring-cloud-gateway-graphql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Albertoimpl%2Fspring-cloud-gateway-graphql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Albertoimpl%2Fspring-cloud-gateway-graphql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Albertoimpl%2Fspring-cloud-gateway-graphql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Albertoimpl","download_url":"https://codeload.github.com/Albertoimpl/spring-cloud-gateway-graphql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245122784,"owners_count":20564370,"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","java","spring","spring-cloud-gateway"],"created_at":"2024-10-13T10:27:50.958Z","updated_at":"2025-03-23T15:31:24.290Z","avatar_url":"https://github.com/Albertoimpl.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= Spring Cloud Gateway and GraphQL\n\n\u003e This post was co-authored by https://twitter.com/ilozano2[Ignacio Lozano] and https://twitter.com/Albertoimpl[Alberto Calleja Ríos]\n\nhttps://spring.io/projects/spring-graphql[Spring GraphQL] is a brand-new project that combines all the greatness of Spring with GraphQL.\n\nHowever, GraphQL comes with a set of problems that can’t be easily solved, for example,  authentication, rate limiting, and observability.\n\nSpring Cloud Gateway can be the perfect partner to your GraphQL applications since it can be easily customized to solve some of the problems GraphQL has.\n\nWe are going to see a full example where we extend a set of applications to expose a graph and add Spring Cloud Gateway on top to solve some of the problems.\n\n== Introduction to GraphQL\n\nGraphQL is an Open Source data query and manipulation language for APIs and a runtime for fulfilling queries with existing data based on a graph structure that simplifies how the consumer asks and receives the data it wants from a single endpoint.\n\nSome benefits are a reduction of useless traffic with unnecessary data, eliminating classic over-fetching, and having a single endpoint where to locate data, it also provides static validation based on the schema that provides feedback to the developer without relying on runtime checks.\n\n== Problems with GraphQL\n\n=== Authentication\n\nAuthentication is the process of determining whether someone or something is, unlike Authorization whose goal is to give someone or something permissions.\n\nIf you don’t protect them with minimal authentication, someone could use your services with malicious intentions.\nGraphQL server doesn't provide a built-in option to enable authentication, leaving your data exposed to any attacker.\n\n=== Rate Limiting\n\nGraphQL allows you to write a server implementation where every field has an independent way of resolving that value. However, without additional consideration, a naive implementation could repeatedly load data from many sources.\n\nThis is solved by a batching technique, where multiple requests for data are collected over a short period of time and then gathered into a single request to an underlying database or microservice but there is still a problem, even if we apply this pattern very well, authenticated calls must introduce a limit for the number of attempts.\n\n=== Observability\n\nSince GraphQL fetches and aggregates data from different sources, complex views can trigger\nmultiple calls to different systems, making it very easy to send a non-performing request that will do a whole graph traversal and, which can impact heavily your application performance.\nBecause of this, understanding how well your requests are performing becomes more important than ever.\n\nHaving data on how well a request is performing can help us identify optimization points and where should and how should we cache our queries.\n\nThe data you have available will depend on the technology you are using to implement your server and on how well you configure it.\n\n== How Spring Cloud Gateway helps\n\nWe are going to show a scenario[https://github.com/Albertoimpl/spring-cloud-gateway-graphql] where we have two REST services that get exposed through a Spring GraphQL server and an instance of Spring Cloud Gateway to help solve the problems mentioned above.\n\nimage::spring-cloud-gateway-graphql.png[spring-cloud-gateway-graphql]\n\n=== Authentication\n\nA Spring Cloud Gateway instance can be configured as an edge gateway providing the first control to the GraphQL server through Authentication.\n\nTo get that control in the Spring Cloud Gateway instance, the next dependency was included:\n\nimplementation 'org.springframework.boot:spring-boot-starter-security'\n\nSpring Security will be applied to the Spring Cloud Gateway instance using the default configuration. For this project, we have added a WebFluxSecurity configuration that will enable basic authentication, and it will disable CORS and CSRF to simplify the demo.\n\nA default user \"alice\" with password \"test\"  was defined to help demonstrate the end-to-end project, and we do not advise any real production environment to follow this approach.\n\nAfter starting the Spring Cloud Gateway instance, all the requests will be protected with basic authentication and any non-authenticated request will be denied by Spring Cloud Gateway.\n\n[source,bash]\n----\ncurl -v http://localhost:8081/library -H'Content-Type: application/json' --data-binary '{\"query\": \"{ books { isbn title }}\"}'\n\n\n\u003c HTTP/1.1 401 Unauthorized\n\u003c WWW-Authenticate: Basic realm=\"Realm\"\n\n----\n\n=== Rate Limiting\n\nThe best place to introduce a rate limit is in your API gateway.\n\nWe are going to add a dependency to Redis to benefit from the already existing RequestRateLimiter that uses the canonical algorithm for rate-limiting of a token bucket.\n\n[source,groovy]\n----\n// Rate Limit\nimplementation \"org.springframework.boot:spring-boot-starter-data-redis-reactive\"\n----\n\nIn that filter, we can customize values, such as, the burst capacity, which is the token bucket maximum capacity, or in other words, how many requests are allowed per second, and the replenish rate, which is how many tokens are added to the bucket every second.\n\n[source,yaml]\n----\n  filters:\n    - RewritePath=/library, /graphql\n    - name: RequestRateLimiter\n      args:\n        redis-rate-limiter.replenishRate: 1\n        redis-rate-limiter.burstCapacity: 1\n----\n\nWe are going to set both of them to 1 in this example, so we can have a steady rate of one request per second.\n\n[source,bash]\n----\ncurl -X POST \\\n-H \"Content-Type: application/json\" \\\n-H \"Authorization: Basic YWxpY2U6dGVzdA==\" \\\n-d '{\"query\": \"{books { title authors {firstName lastName } } }\"}' \\\nhttp://localhost:8081/library -i\n\nHTTP/1.1 200 OK\ntransfer-encoding: chunked\nX-RateLimit-Remaining: 0\nX-RateLimit-Requested-Tokens: 1\nX-RateLimit-Burst-Capacity: 1\nX-RateLimit-Replenish-Rate: 1\nContent-Type: application/json\nDate: Tue, 14 Dec 2021 11:22:17 GMT\n----\n\nAnd if we try again within the same second\n\n[source,bash]\n----\ncurl -X POST \\\n-H \"Content-Type: application/json\" \\\n-H \"Authorization: Basic YWxpY2U6dGVzdA==\" \\\n-d '{\"query\": \"{books { title authors {firstName       lastName } } }\"}' \\\nhttp://localhost:8081/library -i\nHTTP/1.1 429 Too Many Requests\nX-RateLimit-Remaining: 0\nX-RateLimit-Requested-Tokens: 1\nX-RateLimit-Burst-Capacity: 1\nX-RateLimit-Replenish-Rate: 1\ncontent-length: 0\n----\n\n=== Observability\n\nThe Spring portfolio has excellent observability support and by adding a couple of dependencies we can have metrics and tracing enabled in our project and automatically exported to an observability platform.\n\nTo help us understand how well our application is performing on each request we are going to add https://docs.wavefront.com/wavefront_springboot.html[Spring Boot Wavefront] for metrics and https://spring.io/projects/spring-cloud-sleuth[Spring Cloud Sleuth] for tracing.\nIf you want to learn more about them, I would recommend taking a look at https://spring.io/blog/2020/04/29/spring-tips-the-wavefront-observability-platform[this Spring tips episode].\n\nAfter adding the dependencies to our project:\n[source,groovy]\n----\n// Metrics\nimplementation \"com.wavefront:wavefront-spring-boot-starter\"\n// Tracing\nimplementation 'org.springframework.cloud:spring-cloud-starter-sleuth'\n----\n\nIf we restart our applications we see that wavefront automatically created a URL for us:\n\n[source,log]\n----\n\u003e Task :edge-gateway:bootRun\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::                (v2.6.1)\n\n2021-12-03 15:52:11.662  INFO [,,] 49868 --- [           main] e.s.SpringCloudGatewayGraphqlApplication : Starting SpringCloudGatewayGraphqlApplication using Java 11.0.9.1 on rcallejario-a02.vmware.com with PID 49868 (/Users/rcallejarios/workspace/spring-cloud-gateway-graphql/edge-gateway/build/classes/java/main started by rcallejarios in /Users/rcallejarios/workspace/spring-cloud-gateway-graphql/edge-gateway)\n2021-12-03 15:52:11.663  INFO [,,] 49868 --- [           main] e.s.SpringCloudGatewayGraphqlApplication : No active profile set, falling back to default profiles: default\n2021-12-03 15:52:12.808  INFO [,,] 49868 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=d7ad34d1-80f7-3ccf-9f24-f4bd40c9e6ed\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [After]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Before]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Between]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Cookie]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Header]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Host]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Method]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Path]\n2021-12-03 15:52:13.853  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Query]\n2021-12-03 15:52:13.854  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [ReadBody]\n2021-12-03 15:52:13.854  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [RemoteAddr]\n2021-12-03 15:52:13.854  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [Weight]\n2021-12-03 15:52:13.854  INFO [,,] 49868 --- [           main] o.s.c.g.r.RouteDefinitionRouteLocator    : Loaded RoutePredicateFactory [CloudFoundryRouteService]\n2021-12-03 15:52:13.932  INFO [,,] 49868 --- [           main] i.m.c.instrument.push.PushMeterRegistry  : publishing metrics for WavefrontMeterRegistry every 1m\n2021-12-03 15:52:14.262  INFO [,,] 49868 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'\n2021-12-03 15:52:14.682  INFO [,,] 49868 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8081\n2021-12-03 15:52:14.755  INFO [,,] 49868 --- [           main] e.s.SpringCloudGatewayGraphqlApplication : Started SpringCloudGatewayGraphqlApplication in 4.538 seconds (JVM running for 4.905)\n\nYour existing Wavefront account information has been restored from disk.\n\nTo share this account, make sure the following is added to your configuration:\n\n        management.metrics.export.wavefront.api-token=12345678-026e-4355-9086-2xg48cfc1234\nmanagement.metrics.export.wavefront.uri=https://wavefront.surf\n\nConnect to your Wavefront dashboard using this one-time use link:\nhttps://wavefront.surf/us/tLcTcTPxcD\n----\n\nIf we follow the link we can see a very detailed dashboard for Spring Boot applications and, on top, a link that states that traces were detected.\n\nimage::observability-metrics.png[metrics]\n\nIf we follow that link we can see a nice view of the applications that are connected and how long did it take per request.\n\nimage::observability-traces.png[traces]\n\nBy adding the dependencies to the services, we can now exactly see how long does it take for each query to be executed, making it very easy to identify optimization points.\n\n== Getting started\n\nThe full project can be found in https://github.com/Albertoimpl/spring-cloud-gateway-graphql\n\nAnd to get started with it, we can execute the following commands\n\nFirst, we need to start redis for our rate limit:\n\n[source,bash]\n----\ndocker run -p6379:6379 redis:5.0.9-alpine\n----\n\nThen, we can start all the applications with:\n\n[source,bash]\n----\n./gradlew bootRun --parallel\n----\n\nNote: `--parallel` is needed for running a multi-gradle spring boot project. Alternatively, the projects author-service, book-service, graphql-server, and edge-gateway can be run individually running the command in each project folder.\n\nAfter a few seconds you will have locally the following projects running:\nsome time to get all those projects running in your local machine:\n\n* GraphQL Server on port 8080 with GraphQL endpoint: http://localhost:8080/graphql accepting POST requests\n\n* Authors REST Service on port 8091: http://localhost:8091/authors\n\n* Books REST Service on port 8090: http://localhost:8090/books\n\n* Edge Spring Cloud Gateway on port 8081\nRedirecting route http://localhost:8080/library to GraphQL endpoint\nActuator enabled on http://localhost:8081/actuator\n\nIt is important to know that in a production environment you only want to expose the Spring Cloud Gateway, which is acting as an edge gateway, and it will protect the GraphQL API. If you run the projects locally using Gradle, all the endpoints can be reached.\n\nOn the other hand, GraphQL introspection and GraphiQL client have been disabled using the next Spring Boot GraphQL properties `graphql.tools.introspection-enabled: false` and  `graphql.graphiql.enabled: false`.\n\n=== GraphQL queries\n\nThe next queries can be requested to the `http://localhost:8080/library` Spring Cloud Gateway route.\n\n==== Getting all the authors\n\n[source,bash]\n----\ncurl -v http://localhost:8081/library \\\n-H'Content-Type: application/json' \\\n-H'Authorization: Basic YWxpY2U6dGVzdA==' \\\n--data-binary '{\"query\": \"{ authors { firstName lastName picture email phone } }\"}'\n----\n\n[source,bash]\n----\n{\n  \"data\": {\n    \"authors\": [\n      {\n        \"firstName\": \"W. Frank\",\n        \"lastName\": \"Ableson\",\n        \"picture\": \"picture\",\n        \"email\": \"email\",\n        \"phone\": \"phone\"\n      },\n      {\n        \"firstName\": \"Charlie\",\n        \"lastName\": \"Collins\",\n        \"picture\": \"picture\",\n        \"email\": \"email\",\n        \"phone\": \"phone\"\n      },\n...\n----\n\n==== Getting all the authors' names\n\n[source,bash]\n----\ncurl -v http://localhost:8081/library \\\n-H'Content-Type: application/json' \\\n-H'Authorization: Basic YWxpY2U6dGVzdA==' \\\n--data-binary '{\"query\": \"{ books { isbn title pageCount authors { firstName lastName picture email phone } } }\"}'\n----\n\n[source,bash]\n----\n{\n  \"data\": {\n    \"books\": [\n      {\n        \"isbn\": \"1933988673\",\n        \"title\": \"Unlocking Android\",\n        \"pageCount\": 416,\n        \"authors\": [\n          {\n            \"firstName\": \"W. Frank\",\n            \"lastName\": \"Ableson\"\n          },\n          {\n            \"firstName\": \"Charlie\",\n            \"lastName\": \"Collins\"\n          },\n          {\n            \"firstName\": \"Robi\",\n            \"lastName\": \"Sen\"\n          }\n        ]\n      },\n...\n----\n\n==== Getting all the books without authors\n\n[source,bash]\n----\ncurl -v http://localhost:8081/library \\\n-H'Content-Type: application/json' \\\n-H'Authorization: Basic YWxpY2U6dGVzdA==' \\\n--data-binary '{\"query\": \"{ books { isbn title pageCount }}\"}'\n----\n\n[source,bash]\n----\n{\n  \"data\": {\n    \"books\": [\n      {\n        \"isbn\": \"1933988673\",\n        \"title\": \"Unlocking Android\",\n        \"pageCount\": 416\n      },\n      {\n        \"isbn\": \"1935182722\",\n        \"title\": \"Android in Action, Second Edition\",\n        \"pageCount\": 592\n      },\n...\n----\n\n== Next Steps\n\nIn this post, we’ve looked at a few examples of how to solve some of the main problems GraphQL has and how they can be solved using Spring Cloud Gateway.\nWe would love to know what other usages you’ve found to be helpful in your experiences.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertoimpl%2Fspring-cloud-gateway-graphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falbertoimpl%2Fspring-cloud-gateway-graphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertoimpl%2Fspring-cloud-gateway-graphql/lists"}