Skip to content

Commit

Permalink
Merge pull request #5530 from ThisaruGuruge/graphql-query-complexity
Browse files Browse the repository at this point in the history
Add GraphQL Query Complexity BBE
  • Loading branch information
ThisaruGuruge authored Aug 2, 2024
2 parents 75d5a02 + f296752 commit 16ebe56
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query {
profile(id: 1) {
name
age
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query {
profile1: profile(id: 1) {
name
age
}
profile2: profile(id: 2) {
age
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import ballerina/graphql;

// Defines a service class to use as an object in the GraphQL service.
service class Profile {
private final string name;
private final int age;

function init(string name, int age) {
self.name = name;
self.age = age;
}

@graphql:ResourceConfig {
complexity: 1
}
resource function get name() returns string {
return self.name;
}

@graphql:ResourceConfig {
complexity: 10
}
resource function get age() returns int {
return self.age;
}

// Default complexity will be applied
resource function get isAdult() returns boolean {
return self.age > 21;
}
}

@graphql:ServiceConfig {
// The queryComplexityConfig is used to define the values for the maximum query complexity,
// default field complexity, and whether to return an error or warning when the maximum
// complexity is exceeded.
queryComplexityConfig: {
maxComplexity: 50, // Maximum complexity allowed for a query
defaultFieldComplexity: 2 // Default complexity for a field
}
}
service graphql:Service /graphql on new graphql:Listener(9090) {

@graphql:ResourceConfig {
complexity: 20 // Assigning a complexity value to the `profile` field
}
resource function get profile(@graphql:ID int id) returns Profile {
// Return a dummy profile object
return new ("Walter White", 50);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile(id: 1) { name age } }" }' 'http://localhost:9090/graphql'
{"data":{"profile":{"name":"Walter White", "age":50}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
curl -X POST -H "Content-type: application/json" -H "scope: admin" -d '{ "query": "{ profile1: profile(id: 1) { name age } profile2: profile(id: 2) { name age } }" }' 'http://localhost:9090/graphql'
{"errors":[{"message":"The operation exceeds the maximum query complexity threshold. Maximum allowed complexity: 50, actual complexity: 62", "locations":[{"line":1, "column":1}]}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# GraphQL service - Query Complexity

a `graphql:Service` can be secured by limiting the complexity of the operations that can be executed. This can be done by setting a maximum complexity threshold for a given service. The query complexity is calculated by assigning a complexity value to each field in the GraphQL schema. The complexity of an operation is the sum of the complexity values of the fields in the operation.

::: code graphql_service_query_complexity.bal :::

Run the service by executing the command below.

::: out graphql_service_query_complexity.server.out :::

Send the following document to the GraphQL endpoint to test the service.

::: code graphql_service_query_complexity.1.graphql :::

To send the document, execute the following cURL command in a separate terminal.

::: out graphql_service_query_complexity.client.1.out :::

As shown in the output above, the query is executed without any issues. Now, send the following document to the GraphQL endpoint.

::: code graphql_service_query_complexity.2.graphql :::

To send the document, execute the following cURL command in a separate terminal.

::: out graphql_service_query_complexity.client.2.out :::

This will result in an error as the query complexity exceeds the maximum complexity threshold set for the service.

>**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/).
## Related links

- [`graphql:ServiceConfig` record - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ServiceConfig)
- [GraphQL `queryComplexityConfiguration` - Specification](/spec/graphql/#7110-query-complexity-configurations)
- [GraphQL query complexity validation - Specification](/spec/graphql/#1091-query-complexity-validation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: This example demonstrates securing a GraphQL service query complexity analysis.
keywords: ballerina, ballerina by example, bbe, graphql, service, security, query complexity
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ bal run graphql_service_query_complexity.bal
8 changes: 8 additions & 0 deletions examples/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -2601,6 +2601,14 @@
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "Query Complexity",
"url": "graphql-service-query-complexity",
"verifyBuild": true,
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ stdlibWebsubVersion=2.11.0
stdlibWebsubhubVersion=1.11.0

# Stdlib Level 07
stdlibGraphqlVersion=1.12.0
stdlibGraphqlVersion=1.14.0-20240801-114000-e4337a2
stdlibSqlVersion=1.13.0

# Persist Tool
Expand Down

0 comments on commit 16ebe56

Please sign in to comment.