0% found this document useful (0 votes)
40 views41 pages

Graphqlconf

The document discusses declarative configuration as an alternative to imperative or programmatic configuration. It provides examples of declarative configuration in Kubernetes YAML, Terraform, and Docker Compose files. The key advantages of declarative configuration are clarity in API definition, reduced boilerplate code, and easier maintenance and versioning.

Uploaded by

xm6qi4v7nv9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
40 views41 pages

Graphqlconf

The document discusses declarative configuration as an alternative to imperative or programmatic configuration. It provides examples of declarative configuration in Kubernetes YAML, Terraform, and Docker Compose files. The key advantages of declarative configuration are clarity in API definition, reduced boilerplate code, and easier maintenance and versioning.

Uploaded by

xm6qi4v7nv9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

Supercharging

GraphQL with
Declarative
Configuration

Sai Ekbote
● Declarative configuration means saying what you want.

● not how to do it.

● Kubernetes YAML Manifests

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
template:
spec:
containers:
- name: my-app-container
image: my-app-image:1.0
resources:
limits:
cpu: "0.5"
memory: "512Mi"
● Terraform Infrastructure-as-Code (IAC)
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
}

resource "aws_security_group" "example" {


name = "example"
description = "Allow incoming traffic on port 80 and 22"

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
● Docker Compose

version: '3'
services:
web:
image: nginx:latest
ports:
- "80:80"
app:
image: my-app:latest
ports:
- "8080:8080"
● 🍕 Ordering a Pizza
Advantages of Declarative Configuration

● Declarative configuration offers several advantages:


● 1. Clarity in API definition.
● 2. Reduced boilerplate code.
● 3. Easier maintenance and versioning.
?
Declarative
Declarative

type User {
id: ID!
name: String!
email: String!
}

type Query {
user(id: ID!): User
users: [User]
}

Schema Definition Language (SDL)


const resolvers = {
Query: {
ratings: async (_, args) => {
try {
// Extract the API key from the GraphQL
arguments
const { apiKey } = args;

// Define the headers for the HTTP request


const headers = {
Authorization: `Bearer ${apiKey}`,
// You can add other headers as needed
};

// Perform a GET request to


localhost:8080/ratings with headers
const response = await
axios.get('http://localhost:8080/ratings', {
headers,
}); </ Resolvers>
// Assuming the response data is an array of
ratings
const ratings = response.data;

// Transform the response data (e.g.,


multiply each rating value by 2)
const transformedRatings =
ratings.map((rating) => ({
...rating,
value: rating.value * 2,
}));

return transformedRatings;
} catch (error) {
console.error('Error fetching ratings:',
error);
throw error;
}
const resolvers = {
Query: {
ratings: async (_, args) => {
try {
// Extract the API key from the GraphQL
arguments
const { apiKey } import
= args;
graphql.kickstart.tools.GraphQLQueryResolver;
// Define the headers for the HTTP request
import module Types const headers = {public class QueryResolver implements
class QueryType Authorization: `Bearer ${apiKey}`,
graphql.kickstart.tools.GraphQLQueryResolver;
< Types::BaseObject
GraphQLQueryResolver {
// You can add other
field :get_user, UserType, null: headers
true as needed
public User getUser(int id) {
public class
do }; implements
QueryResolver // Your logic to fetch user data by id
GraphQLQueryResolver
description
{ "Get a user by ID" return getUserById(id);
// Perform a GET request to
public Userargument
getUser(int
:id, id)
Int, { required: }true
localhost:8080/ratings with headers
// Your
end logic toconst
fetch user data
response by id
= await
return getUserById(id); // Example method
axios.get('http://localhost:8080/ratings', { to fetch user data
} def get_user(id:)
headers, private User getUserById(int id) {
# Your logic
}); toimport
fetch graphene
user data by // Implement your data retrieval logic
// Example
id
private User
method to fetch user data
// Assuming
get_user_by_id(id)
getUserById(int
ratings
// Implement
end
the
classid)
here
{ response data is an array
Query(graphene.ObjectType):
your data retrieval
get_user logic
// Return a User
= graphene.Field(UserType,
found
</
of object or null if not Resolvers>
here end const ratings = response.data;
id=graphene.Int())}
//
endReturn a User object or null} if not
// Transform the response data (e.g., multiply
found
each rating value def
by 2)resolve_get_user(self, info, id):
} # Example methodconst
to fetch user data logic
# Your
transformedRatings to fetch user data by id
= ratings.map((rating)
} def get_user_by_id(id)
=> ({ return get_user_by_id(id)
# Implement your data retrieval logic
...rating,
here # Example
value: function
rating.value * 2, to fetch user data
# Return a User object
})); or nil if not
def get_user_by_id(id):
found # Implement your data retrieval logic here
end return transformedRatings;
# Return a User object or None if not found
} catch (error) {
console.error('Error fetching ratings:',
error);
throw error;
}
},
},
};
Imperative
Declarative
(Programmatic)
apiVersion: graphql.gloo.solo.io/v1beta1
const resolvers = {
Query: {
ratings: async (_, args) => {
try {
// Extract the API key from the GraphQL arguments
const { apiKey } = args;
kind: GraphQLResolverMap
// Define the headers for the HTTP request
const headers = { import spec:
Authorization: `Bearer ${apiKey}`,
graphql.kickstart.tools.GraphQLQueryResolver;
Query:
// You can add other headers as needed
};
public class QueryResolver implements
import module Types
// Perform GraphQLQueryResolver
a GET request {
to localhost:8080/ratings
graphql.kickstart.tools.GraphQLQueryResolver;
class QueryType < Types::BaseObject
with headers
field :get_user, UserType, null: true
const response = await
public User getUser(int id) {
// Your logic to fetch user data by id
GetPlans:
public class
do QueryResolver implements
axios.get('http://localhost:8080/ratings',
return{ getUserById(id);
GraphQLQueryResolver
description
public Userargument
{
getUser(int
"Get a user by ID"
headers,
:id, id)
});
}
Int,{ required: true restResolver:
// Your
end logic to fetch user data by id
// Assuming the response // Example method
of to fetch user data
request:
return getUserById(id); data is an array
ratings private User getUserById(int id) {
} def get_user(id:)
const ratings = response.data;// Implement your data retrieval logic
# Your logic to fetch user data by
here
// Example
id
private User
method to//fetch
get_user_by_id(id)
getUserById(int
rating
userthe
Transform data
value by 2) id) {
response data (e.g., multiply each
// Return a User object or null if not headers:
import graphene
found= ratings.map((rating) =>
// Implement
end yourconst transformedRatings
data retrieval logic
here end
({ }
class Query(graphene.ObjectType):
...rating, } :method: GET
//
endReturn a User object or null if not
get_user *= 2,
value: rating.value graphene.Field(UserType,
found
:path: /v1/ratings
})); id=graphene.Int())
} # Example method to fetch user data
} return transformedRatings;
def get_user_by_id(id) def
{ resolve_get_user(self, info, id):
# Implement }your
catchdata
(error)
retrieval logic
here
# Your
console.error('Error
throw error;
logicratings:',
fetching to fetcherror);
return get_user_by_id(id)
user data by id
destination:
# Return a User
} object or nil if not
found
end },
}, # Example function to fetch user data
def get_user_by_id(id):
host: plans.prod.svc.cluster.local
};
# Implement your data retrieval logic here
# Return a User object or None if not found resolverResultTransform:
jq: .data.[] | .value = .value * 2
The Evolution of GraphQL

GraphQL GraphQL Automatic GraphQL


Imperative Imperative Libraries Declarative
Section 2 - Prominent Solutions for
Declarative GraphQL
GraphQL
● Data Source Aggregation: Aggregate data from multiple sources, including REST APIs,
databases into a single GraphQL schema. This simplifies the process of querying and
retrieving data from various services.
● Auto-Generated Resolvers: GraphQL Mesh automatically generates resolvers for your
schema, so you don't have to write custom resolver functions for each data source. This
reduces the amount of boilerplate code you need to create.
● Schema Stitching: It uses schema stitching techniques to combine schemas from
different services, providing a unified API surface for clients. You can also define custom
stitching logic to handle complex data relationships.
● Flexible Configuration: GraphQL Mesh is highly configurable, allowing you to customize
how data is fetched, transformed, and exposed in your GraphQL schema. You can use
plugins and presets to extend its functionality and adapt it to your specific needs.
sources:
- name: Books
handler:
openapi:
source: http://localhost:3002/openapi.json
transforms:
- rename:
renames:
- from:
type: Query
field: AppController_(.*)
to:
type: Query
field: $1
useRegExpForFields: true
Other protocols → GraphQL

Users

Payments </SOAP>

Pricing

Inventory

Employees

github.com/Urigo/graphql-mesh
GraphQL
● Data Source Aggregation: Aggregate data from multiple sources, including REST APIs,
databases into a single GraphQL schema. This simplifies the process of querying and
retrieving data from various services.
● Auto-Generated Resolvers: GraphQL Mesh automatically generates resolvers for your
schema, so you don't have to write custom resolver functions for each data source. This
reduces the amount of boilerplate code you need to create.
● Schema Stitching: It uses schema stitching techniques to combine schemas from
different services, providing a unified API surface for clients. You can also define custom
stitching logic to handle complex data relationships.
● Flexible Configuration: GraphQL Mesh is highly configurable, allowing you to customize
how data is fetched, transformed, and exposed in your GraphQL schema. You can use
plugins and presets to extend its functionality and adapt it to your specific needs.
GraphQL

sources:
- name: Stripe
handler:
openapi:
source:
https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json
endpoint: https://api.stripe.com
operationHeaders:
Authorization: 'Bearer {env.STRIPE_TOKEN}'

documents:
- get-customers.query.graphql
type Query {
anything(message: String): JSON
@rest (
endpoint: "https://httpbin.org/anything"
method: POST
headers: [
{name: "User-Agent", value: "StepZen"}
{name: "X-Api-Key", value: "12345"}
]
)
}
type Query {
myQuery(key1: String, key2: String): RootEntry Request with headers to an endpoint
@rest(
method: POST
endpoint:
"http://dummy.restapiexample.com/api/v1/create"
)
}

POST request to a REST endpoint


type Query {
anything(id: ID, name: String, message: String): JSON
@rest (
endpoint: "https://httpbin.org/anything/users/$id"
method: POST
headers: [
{name: "User-Agent", value: "StepZen"}
{name: "X-Api-Key", value: "12345"}
]
postbody: """
{
"user": {
"name": "{{.Get "name"}}",
"message": "{{.Get "message"}}"
}
}
"""
)
}

POST request with templated body and headers


stepzen import curl "YOUR_ENDPOINT" --header "Authentication: 123"

Automatically generate GraphQL Schemas with resolvers to non-GraphQL backends


Other protocols → GraphQL

Users

Payments </SOAP>

Pricing

Inventory

Employees
● Gloo GraphQL bundles your GraphQL server with a network proxy, adding all the benefits that
you can get from an API Gateway along with your GraphQL server

apiVersion: graphql.gloo.solo.io/v1beta1
kind: GraphQLResolverMap
spec:
Query:
GetPlans:
restResolver:
request:
headers:
:method: GET
:path: /v1/ratings
destination:
host: plans.prod.svc.cluster.local
resolverResultTransform:
jq: .data.[] | .value = .value * 2
Proxy is a GraphQL Server Architecture

Users

Payments </SOAP>

CACHING

Proxy +
Proxy
GraphQL
Pricing
EXTERNAL AUTH

RATE LIMITING Inventory


WAF
Solutions Overview

● GraphQL Mesh:
○ Auto-generate schemas from existing non-GraphQL services
○ Configuration includes declarative transforms on the schema
○ Configuration includes plugins such as Rate limit, CSRF, Query Depth Limiting
● StepZen by IBM:
○ Inline declarative configuration via directives
○ Connect your GraphQL schema to database backends, as well as REST and gRPC backends
○ CLI to automatically generate GraphQL Schemas from existing backensd
● Gloo GraphQL:
○ Auto-generate schemas from existing REST, gRPC, and GraphQL services (via introspection) via CLI
○ Declarative data transforms via jq filter
○ Exists as a part of Envoy network proxy – a common component to many API gateway and service mesh
solutions
Declarative GraphQL with Envoy

● We've taken the concept of declarative GraphQL a step further by implementing a


high-performance GraphQL server as a component of the industry-standard Envoy proxy.

● Discover the magic of combining them!


Proxy + GraphQL Server Architecture

Users

Payments </SOAP>

Proxy GraphQL Server


Pricing

Inventory
Proxy is a GraphQL Server Architecture

Users

Payments</SOAP> </SOAP>

Proxy +
GraphQL Pricing

Inventory
Proxy is a GraphQL Server Architecture

Users

Payments </SOAP>

CACHING

Proxy +
Proxy
GraphQL
Pricing
EXTERNAL AUTH

RATE LIMITING Inventory


WAF
Using a high performance, compiled network proxy as a
GraphQL server isn't very common, so how do we pull it off?

● We're obviously not compiling any GraphQL server code here


● Instead, we configure the GraphQL filter via Declarative Configuration
Implementing GraphQL in a C++ Network
Proxy
GraphQL Support in Envoy
GraphQL Request

Custom Envoy Filter


EXTERNAL AUTH

• Web Application Firewall (WAF)


RATE LIMITING
• Data Loss Prevention (DLP)
gRPC
TRANSCODER
• AWS Lambda
GRAPHQL
• Request and Response Transformation
ROUTER
• SOAP

Upstream
• GraphQL
Service
Declarative GraphQL Configuration
apiVersion: graphql.gloo.solo.io/v1beta1
kind: GraphQLResolverMap
spec:
Query:
type Plan {
GetPlans:
id: ID
pricing: Int restResolver:
payments: [Int] request:
} headers:
:method: GET
type Query {
GetPlans(userId: ID): [Plan] :path: /v1/plans/{$args.user_id}
} destination:
host: plans.prod.svc.cluster.local
resolverResultTransform:
jq: |
.[] | to_entries | .[]
| .pricing = .value.pricing | .id = .key
| .payments = (.value.payments | map( tonumber))
| del(.value, .key)
What if the REST response doesn't align
with the GraphQL schema?
[{ [{
"A23CD_E": { "id": "A23CD_E"
"pricing": 234, "pricing": 234
"payments": [12, 13.45, "45", 52] "payments": [12, 13, 45, 52]
} }]
}]

jq filter

.[] | to_entries | .[] | .pricing = .value.pricing | .id = .key |


.payments = (.value.payments | map( tonumber)) | del(.value, .key)
Config as Code GitOps Repository Kubernetes Clusters

GraphQL
Schemas

Resolver
Map

Deployment

Access
Policies
Recap: Why GraphQL in Service Mesh Proxy

Developer Productivity Declarative Configuration; GitOps

Fewer points of failure GraphQL Type Safety

No network hop between proxy and Platform concerns stay in platform


GraphQL server proxy: simple resolvers
Key Takeaways

● Declarative GraphQL is not just a trend; it's a paradigm shift.

● Explore tools like Gloo GraphQL, GraphQL Mesh, and StepZen to harness the power of
declarative configuration.

● Embrace the advantages of declarative approaches for clearer, more maintainable APIs.

● Pay attention to performance and scalability when implementing declarative GraphQL


systems.
Q&A Session
Thank You

You might also like