Kong Konnect RESTful Admin APIs and AWS AppSync GraphQL Services - Part I: Query
Runtime Groups, Services, Routes, and Plugins
This blog post will work with the Services, Routes, and Plugins created in the
default Runtime Group. Check the documentation to learn more about Runtime Manager and Runtime Groups.Once you have your PAT generated, you can start sending requests to the Kong Konnect Control Plane. For example, you can check your Runtime Groups with:
GraphQL API creation
We're going to get started creating a GraphQL Schema with fundamental types representing the three Kong Objects: Kong Service, Kong Route, and Kong Plugin. The Schema also defines three queries:-
service(name): Service -> returns information about a specific Kong Service
services: [Service]-> returns a list of all Kong Services created in a specific Runtime Group.
runtimegroups: [RuntimeGroup]-> returns all existing Runtime Groups.**
GraphQL API with Empty Schema
Create your GraphQL API with an empty schema first:
aws appsync create-graphql-api --name konnect-controlplane --authentication-type API_KEY --region us-west-1``
The expected output is:
Schema Definition Language (SDL)
Now create a
konnect.graphql file with our Schema including Types and Queries:
You have to encode the Schema in a base64 format:
Use the base64 string to create the Schema for you GraphQL API:
Check the Schema Creation process with:
You can list the Types with:
Or create a SDL file with:
A GraphQL Resolver is a function that converts the GraphQL request and payload to fetch information from the underlying data source with its specific communication mechanism and responds back to the consumer. In our case, the data source is the Konnect Control Plane with its RESTful Admin APIs.
AWS AppSync supports two types of Resolvers:
- Mapping Templates: written in Apache Velocity Template Language (VTL), they are comprised of request and response mapping templates, which contain transformation and execution logic.
For both types of Resolvers, AppSync provides support for multiple data sources including DynamoDB, Lambda functions, Amazon Aurora, Amazon OpenSearch Service. Besides all these data sources, AppSync also supports generic HTTP endpoints, like the ones Kong Konnect uses to expose its RESTful Admin APIs.
Kong Konnect as an AppSync Data Source
All AppSync queries should be sending requests to the Konnect endpoint. So, the very first thing to do is to define an AppSync Data Source based on the endpoint. Note we’re creating an HTTP type Data Source.
You can check your Data Sources with:
RuntimeGroup HTTP Resolver
Now, we should be able to create our Resolvers. To get started, let's create a VTL Resolver for the
runtimegroups Query field. Notice that the Resolver is expecting the Konnect PAT as a Authorization Bearer token:
The data that came from Konnect is available for the Response Mapping Template in the Resolver Context Result Body
($ctx.result.body). Check the AppSync documentation to learn more about Resolver Mapping Template Context.
The code refers to the
.data section located inside the Body to produce the actual GraphQL query response. As an example, here's the output for the request sent to Konnect directly:
AppSync API Key
We’re now ready to send our first GraphQL request to AppSync. However, since the default AppSync Authorization mechanism is an API Key, we need to create one.
You can get your API Key with:
Send a Request
Let's send a request to AppSync. First of all, get the public AppSync API endpoint:
Now, send a request with
curl. Notice we're injecting in the request both AppSync API Key and Kong Konnect PAT. The PAT is injected as a Bearer token, as the Resolver is expecting.
The output should be like this:
The data that came from Kong Konnect is available for the Response Mapping Template in the Resolver Context Result Body
($ctx.result.body). You can check them by submitting a request directly to Konnect:
Services HTTP Resolver
Let's create a second VTL Resolver, now for the
services Query field. As you can see, the Resolver definition is quite similar to the previous one. The main difference is that this time, the Resolver expects the RuntimeGroup Id as a context parameter.
Send a Request
Use the same endpoint and AppSync API Key to send a new request. Note we’re passing the RuntimeGroup id we got from the previous request as a parameter to the Query:
You should get a list of all current Kong Services:
You can check the AWS AppSync console with the Schema and Resolvers defined:
With the two fundamental Resolvers in place, let's work with the third one. The
service Resolver takes as parameters a service name and the runtime groups the service belongs to. The output includes the two main dependencies of the service: Kong Routes and Kong Plugins.
The GraphQL Query Request Structure
To get a better understanding of the Resolver implementation, we’re going to take an evolving approach. That is, considering the full Query, we’re going to solve it breaking with three independent steps: (1) Service, (2) Routes and (3) Plugins.
Send a Request
Let's send the Request with no Resolver first:
Since we don't have any Resolver defined, the output would look like this:
Let's solve the Service section creating a function to be used by our Resolver. Create a file named
service.js with an AppSync Function to process the section. The function takes the two query parameters,
name to consume the specific Konnect endpoint. The Resolver builds the result based on the Konnect response, adding the
runtimegroupid to it, so the functions responsible for the Routes and Plugin Section can use it.
Create the AppSync function with:
Fetch and save your function Id with:
The Resolver also needs basic request and response functions. Create another file named
basic_resolver.js with the functions:
Create the Resolver
Now let's create the Resolver with all functions we've created so far:
Send a Request
If we send the same Request again, we should be able to see the Service Section with its attributes. As expected the Routes and Plugins Sections are still empty.
Now, let's solve the Routes Section with another Resolver. We have to consider two things this time:
- The Routes Section is nested inside the Query structure.
- Since it's a nested section, the Resolver is related to the Service Field, not to the Query Field as the Service Section is.
To solve the Routes Section we’re going to create a function that is able to get both Services dependencies, Kong Routes, and Kong Plugins. The function takes the
serviceid from its parent Service's Resolver available in
ctx.source Context field. It also expects a
dependency field available in the
ctx.stash. Please check the AppSync documentation to learn more about the Resolver Context Object.
Create the function with:
Get and save the function id:
Now we need to create a specific
routes field specific Resolver. The main responsibility of the function is to set the
ctx.stash.dependency field, expected by the
Create the Resolver
The Resolver is ready to be created. The Resolver pipeline includes the previously created
dependency function id:
Send a Request
Send the same request we did before to get the Kong Routes defined for our Service. The output should look like this. Notice that we still need to solve the Plugins Section.
The Plugin Resolver works exactly the same way the Routes Resolver does. In fact, it's going to add the same dependencies function into its pipeline. The only difference is that the Resolver has to set the
ctx.stash.dependency with a different value, in this case,
Create the Resolver
Again, the command to create the Resolver is very similar to the one we ran for the Routes Resolver.
Send a Request
Finally, our request's output should include all Plugins enabled to our Service:
Kong Konnect GraphQL Rate Limiting Advanced Plugin
With all AppSync Resolvers in place we’re able to send as many GraphQL requests as we want. In a real-world scenario, we should have some policies to control that.
In this section, we’re going to deploy a Kong Konnect Data Plane in front of the AWS AppSync in order to enforce some Rate Limiting policies to control the number of requests AppSync should process in a specific period of time.
The final topology would look like this:
In fact, this is a circular deployment where the Konnect Control Plane manages not just the Data Plane but also the Kong Objects (Services, Routes, and Plugins) responsible for controlling the AppSync exposure.
Konnect Runtime Instance
A Konnect Runtime Instance is a single self-managed instance of Kong Gateway that functions as a Data Plane. In Kong Konnect, Runtime Instances are part of Runtime Groups and expose all Kong Objects (Services, Routes, Consumers, Plugins, etc) defined in the Runtime Group it belongs to.
Konnect Runtime Instance deployment
The first step is to deploy a Konnect Runtime Instance. Konnect supports a variety of platforms to run Runtime Instances including Kubernetes, Docker, Linux-based OSes, etc. Please check the Installation Options documentation page to learn more about Runtime Instances.
For this blog post, we’re going to deploy our Runtime Instance on an Amazon EKS Cluster. Click on the Amazon EKS documentation page to learn how to create a cluster with eksctl, the official CLI for Amazon EKS.
After getting your EKS Cluster up and running, log in to Kong Konnect.
Go to "Runtime Manager" -> "default" -> "+ New Runtime Instance" -> "Kubernetes". Click on "Generate Certificate" and save the certificate and private key into corresponding files. For example:
Injecting key and digital certificate
Create a Kubernetes namespace for the data plane.
Create secrets to digital certificate and key.
Konnect Data Plane
Copy and paste this text into a file called
values.yaml on your local machine.
Use Helm to deploy the data plane with endpoints provided by Kong Konnect control plane:
Checking the installation
Use kubectl to check the Kubernetes deployment
You should be able to see the new runtime in Kong Konnect control plane:
Send a request to the runtime
Use the load balancer provisioned by AWS to send requests to the data plane:
Kong Gateway Service and Route
Now, let's create a new Kong Service and Route to expose the AWS AppSync endpoint with the Konnect Runtime Instance. The Kong Service will be created in the same Runtime Group where our existing Kong Objects, in our case, the
default Runtime Group. You can use the Konnect GUI if you like or, again, the Konnect RESTful API:
Kong Gateway Service
graphqlservice Kong Gateway Service is based on the AWS AppSync endpoint. Notice that we inject the Konnect PAT in the request:
Get your new Gateway Service Id with:
Use the Service Id to define the Kong Route:
You can check the Gateway and Route in the Konnect GUI:
AWS Lambda as the AppSync Authorization Method
We’re going to explore another AppSync Authorization Method, replacing the existing one based on API Key to another using an AWS Lambda Function. With this, we’re able to remove the
x-api-key header from the request, leaving both AppSync and Konnect Control Plane to check the Bearer token with the PAT instead.
The AWS Lambda Function
First of all, create an IAM role to be used by the Lambda function. Create a
role.json file with:
Create the IAM Role with the following command:
AWS Lambda Function
Create another file, named
isauthorized.py, with the actual function code written in Python. Notice the function is extremely simple, just checking if the request has a Bearer token injected. Please, bear in mind the Authorization logic can go as complex as we want.
Zip the file:
konnectIsAuthorized Lambda function:
You can invoke the function to test it:
Change the GraphQL API Authorization Mode
Now we’re ready to replace the existing authorization mode with the Lambda function based one:
Send new Requests to the Konnect Runtime Instance
We should get a similar response if we send a request to the Runtime Instance. For a simple test, the
curl command uses the
-k option to accept the Konnect Self-Signed Digital Certificate. Moreover, we don't need to inject the
x-api-key AppSync Header, since our Lambda function is responsible for the AppSync Authorization mode.
GraphQL Rate Limiting Advanced Plugin
So far, we have deployed the Konnect Runtime Instance in front of the AWS AppSync API. However, there's no policy controlling how the GraphQL API has been consumed. Now, it’s time to enable the GraphQL Rate Limiting Advanced Plugin to our Kong Service.
The GraphQL Rate Limiting Advanced plugin provides rate limiting for GraphQL queries. The GraphQL Rate Limiting plugin extends the Rate Limiting Advanced plugin.
Due to the nature of client-specified GraphQL queries, the same HTTP request to the same URL with the same method can vary greatly in cost depending on the semantics of the GraphQL operation in the body. A common pattern to protect your GraphQL API is then to analyze and assign costs to incoming GraphQL queries and rate limit the consumer’s cost for a given time window.
GraphQL query costs are evaluated by introspecting the endpoint’s GraphQL schema and applying cost decoration to parts of the schema tree. Learn more about GraphQL Queries Cost Management on the specific GraphQL Rate Limiting Advanced Plugin documentation page.
As a best practice for Runtime Instance deployments, we should externalize the limits to an external Redis infrastructure. In this sense, all Kubernetes replicas of the deployment will rely on the same limits. To support a configuration like this, the GraphQL Rate Limiting Advanced Plugin provides multiple strategies, including Redis.
You can have your Redis infrastructure running on a totally and independent environment, for example, leveraging the AWS ElastiCache for Redis. To have a simpler deployment, you can run Redis in the same EKS Cluster with this declaration.
Apply GraphQL Rate Limiting Advanced plugin to the Service
Finally, let's enable the plugin to our Service. Notice we’re using a simple limit and window_size to exercise the rate-limiting policy. Also, we can tell where the Redis endpoint is using the Kubernetes Service FQDN,
Send new Requests to the Runtime Instance
This time we’re using the
-v option for
curl to see the Rate Limiting related Headers:
If you keep on sending requests to Konnect Runtime Instance you'll get a 429 error code:
This blog post described Kong Konnect’s capabilities to:
- Embedded its RESTful Admin API to support GraphQL for the Konnect Control Plane administration.
- Expose and protect an AWS AppSync API with enterprise-wide policies implemented by the Konnect Runtime Instance.
The next blog post of this series will describe how to implement other GraphQL operations: Mutations and Subscriptions. Stay tuned!