Skip to content

Commit

Permalink
Merge pull request ballerina-platform#5188 from Nuvindu/master
Browse files Browse the repository at this point in the history
Add BBEs for GraphQL server-side Caching
  • Loading branch information
praneesha authored Feb 13, 2024
2 parents 63708aa + 1ced4b9 commit dc68a98
Show file tree
Hide file tree
Showing 26 changed files with 337 additions and 1 deletion.
25 changes: 25 additions & 0 deletions examples/graphql-id-scalar-type/graphql_id_scalar_type.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ballerina/graphql;

// Defines a `record` type to use as an object in the GraphQL service.
public type User readonly & record {|
@graphql:ID int id;
string name;
int age;
|};

// Defines an in-memory table to store the users.
table<User> key(id) users = table [
{id: 1, name: "Walter White", age: 50},
{id: 2, name: "Jesse Pinkman", age: 25}
];

service /graphql on new graphql:Listener(9090) {

// Returns the user for the given `id`.
resource function get user(@graphql:ID int id) returns User|error {
if users.hasKey(id) {
return users.get(id);
}
return error(string `User with the ${id} not found`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ user(id: 1){ id name } }" }' 'http://localhost:9090/graphql'
{"data":{"user":{"id":1, "name":"Walter White"}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
user(id: 1) {
id
name
}
}
23 changes: 23 additions & 0 deletions examples/graphql-id-scalar-type/graphql_id_scalar_type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GraphQL service - ID scalar type

The Ballerina `graphql` module provides support for the `ID` scalar type in GraphQL. The `ID` type is used to represent unique identifiers within a GraphQL schema. It is a built-in scalar type that can be utilized to define input parameters for GraphQL fields. Applying the `@graphql:ID` annotation to specific fields indicates that they correspond to the GraphQL `ID` scalar type. Consequently, the generated schema will display the field type as `ID` regardless of the actual type of the field.

::: code graphql_id_scalar_type.bal :::

Run the service by executing the following command.

::: out graphql_id_scalar_type.server.out :::

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

::: code graphql_id_scalar_type.graphql :::

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

::: out graphql_id_scalar_type.client.out :::

>**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/).
## Related links
- [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest)
- [GraphQL ID scalar type - Specification](/spec/graphql/#415-id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: This example demonstrates using the GraphQL ID scalar type as a unique identifier.
keywords: ballerina, ballerina by example, bbe, graphql, scalar, id
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ bal run graphql_id_scalar.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import ballerina/graphql;

// Defines a `record` type to use as an object in the GraphQL service.
type User readonly & record {|
int id;
string name;
int age;
|};

// Defines an in-memory table to store the profiles.
table<User> key(id) users = table [
{id: 1, name: "Walter White", age: 50},
{id: 2, name: "Jesse Pinkman", age: 25}
];

service /graphql on new graphql:Listener(9090) {

@graphql:ResourceConfig {
cacheConfig: {}
}
resource function get user(int id) returns User|error {
if users.hasKey(id) {
return users.get(id);
}
return error(string `User with the ${id} not found`);
}

@graphql:ResourceConfig {
cacheConfig: {}
}
resource function get users() returns User[] {
return users.toArray();
}

// Updates a user.
remote function updateUser(graphql:Context context, int id, string name,
int age) returns User|error {
// `invalidate()` is used to invalidate the cache for the given field.
check context.invalidate("user");
if users.hasKey(id) {
_ = users.remove(id);
User user = {id: id, name: name, age: age};
users.add(user);
return user;
}
return error(string `User with the ${id} not found`);
}

// Deletes a user.
remote function deleteUser(graphql:Context context, int id) returns User|error {
// `invalidateAll()` is used to invalidate all the caches in the service.
check context.invalidateAll();
if users.hasKey(id) {
User user = users.remove(id);
return user;
}
return error(string `User with the ${id} not found`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -X POST -H "Content-type: application/json" -d '{ "query": "mutation updateUser { updateUser(id: 1, name: \"Heisenberg\", age: 52){id name age} }" }' 'http://localhost:9090/graphql'
{"data":{"updateUser":{"id":1, "name":"Heisenberg", "age":52}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation updateUser {
updateUser(id: 1, name: "Heisenberg" age: 52) {
id
name
age
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GraphQL service - Cache invalidation

The Ballerina `graphql` module provides functionality for cache invalidation. The `invalidate()` and `invalidateAll()` APIs in the `graphql:Context` can be used to invalidate caches in a `graphql:Service`. The `invalidate()` API supports the cache invalidation of a specific field by providing the full path of the field separated by a full stop(`.`). For example, `invalidate("field.subfield.anotherSubfield")`. Conversely, the `invalidateAll()` API invalidates all caches within the `graphql:Service`.

::: code graphql_service_cache_invalidation.bal :::

Run the service by executing the following command.

::: out graphql_service_cache_invalidation.server.out :::

Then, send the following document to update the user.

::: code graphql_service_cache_invalidation.graphql :::

To send the document, execute the following cURL command.

::: out graphql_service_cache_invalidation.client.out :::

>**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/).
## Related links
- [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest)
- [GraphQL Cache invalidation - Specification](/spec/graphql/#10713-cache-invalidation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: This example demonstrates the cache invalidation of a GraphQL service.
keywords: ballerina, ballerina by example, graphql, cache, invalidation, server-side caching
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ bal run graphql_service_cache_invalidation.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ballerina/graphql;

// Defines a `record` type to use as an object in the GraphQL service.
type User readonly & record {|
int id;
string name;
int age;
|};

// Defines an in-memory table to store the users.
table<User> key(id) users = table [
{id: 1, name: "Walter White", age: 50},
{id: 2, name: "Jesse Pinkman", age: 25}
];

service /graphql on new graphql:Listener(9090) {

// The `cacheConfig` in the `graphql:ResourceConfig` annotation is used to
// configure the cache for a specific field in the GraphQL service.
// (default: {enabled: true, maxAge: 60, maxSize: 120})
@graphql:ResourceConfig {
cacheConfig: {}
}
resource function get name(int id) returns string|error {
if users.hasKey(id) {
return users.get(id).name;
}
return error(string `User with the ${id} not found`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name(id: 1) }" }' 'http://localhost:9090/graphql'
{"data":{"name":"Walter White"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
name(id: 1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GraphQL service - Field-level caching

The Ballerina `graphql` module provides the capability to enable GraphQL caching, which can be applied at either the field or operation level. To enable caching at the field level, the `cacheConfig` field in the `graphql:ResourceConfig` annotation can be used on a resource method within a `graphql:Service`. By setting this configuration, caching is enabled for the specified GraphQL field, and it can override the cache configurations set at the operation level.

::: code graphql_service_field_level_caching.bal :::

Run the service by executing the following command.

::: out graphql_service_field_level_caching.server.out :::

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

::: code graphql_service_field_level_caching.graphql :::

To send the document, execute the following cURL command.

::: out graphql_service_field_level_caching.client.out :::

>**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/).
## Related links
- [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest)
- [GraphQL Field-level caching - Specification](/spec/graphql/#10712-field-level-caching)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: This example demonstrates the field-level caching functionality within a GraphQL service.
keywords: ballerina, ballerina by example, graphql, cache, server-side caching
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ bal run graphql_service_field_level_caching.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import ballerina/graphql;

// Defines a `record` type to use as an object in the GraphQL service.
type User readonly & record {|
int id;
string name;
int age;
|};

// Defines an in-memory table to store the users.
table<User> key(id) users = table [
{id: 1, name: "Walter White", age: 50},
{id: 2, name: "Jesse Pinkman", age: 25}
];

// The `cacheConfig` in the `graphql:ServiceConfig` annotation is used to
// configure the cache for the GraphQL service.
// (default: {enabled: true, maxAge: 60, maxSize: 120})
@graphql:ServiceConfig {
cacheConfig: {}
}
service /graphql on new graphql:Listener(9090) {

resource function get name(int id) returns string|error {
if users.hasKey(id) {
return users.get(id).name;
}
return error(string `User with the ${id} not found`);
}

// The `enabled` field enables/disables the cache for the field. (default: true)
@graphql:ResourceConfig {
cacheConfig: {
enabled: false
}
}
resource function get user(int id) returns User|error {
if users.hasKey(id) {
return users.get(id);
}
return error(string `User with the ${id} not found`);
}

// The `maxAge` field sets the maximum age of the cache in seconds. (default: 60)
// The `maxSize` field indicates the maximum capacity of the cache table by entries.
// (default: 120)
@graphql:ResourceConfig {
cacheConfig: {
maxAge: 600,
maxSize: 100
}
}
resource function get age(int id) returns int|error {
if users.hasKey(id) {
return users.get(id).age;
}
return error(string `User with the ${id} not found`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$ curl -X POST -H "Content-type: application/json" -d '{ "query": "{ name(id: 1) }" }' 'http://localhost:9090/graphql'
{"data":{"name":"Walter White"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
name(id: 1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GraphQL service - Operation-level caching

The Ballerina `graphql` module provides the capability to enable GraphQL caching. GraphQL caching can be enabled at either the field level or the operation level. To enable caching at the operation level, the `cacheConfig` field in the `graphql:ServiceConfig` annotation can be applied to a service. This configuration will enable caching for all resource paths within the `graphql:Service`.

::: code graphql_service_operation_level_caching.bal :::

Run the service by executing the following command.

::: out graphql_service_operation_level_caching.server.out :::

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

::: code graphql_service_operation_level_caching.graphql :::

To send the document, execute the following cURL command.

::: out graphql_service_operation_level_caching.client.out :::

>**Tip:** You can invoke the above service via the [GraphQL client](/learn/by-example/graphql-client-query-endpoint/).
## Related links
- [`graphql` module - API documentation](https://lib.ballerina.io/ballerina/graphql/latest)
- [GraphQL Operation-level caching - Specification](/spec/graphql/#10711-operation-level-caching)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: This example demonstrates the caching of a GraphQL service at the operation level.
keywords: ballerina, ballerina by example, graphql, cache, server-side caching
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ bal run graphql_service_operation_level_caching.bal
32 changes: 32 additions & 0 deletions examples/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -2370,6 +2370,14 @@
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "ID scalar Type",
"url": "graphql-id-scalar-type",
"verifyBuild": true,
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "Interfaces",
"url": "graphql-interfaces",
Expand Down Expand Up @@ -2513,6 +2521,30 @@
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "Operation-level caching",
"url": "graphql-service-operation-level-caching",
"verifyBuild": true,
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "Field-level caching",
"url": "graphql-service-field-level-caching",
"verifyBuild": true,
"verifyOutput": false,
"disablePlayground": true,
"isLearnByExample": false
},
{
"name": "Cache invalidation",
"url": "graphql-service-cache-invalidation",
"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 @@ -64,7 +64,7 @@ stdlibWebsubVersion=2.10.0-20230831-211200-c6f33c4
stdlibWebsubhubVersion=1.10.0-20230831-210000-695f161

# Stdlib Level 07
stdlibGraphqlVersion=1.10.0-20230914-151300-376b3a9
stdlibGraphqlVersion=1.11.0-20240212-151600-33c9bfd
stdlibSqlVersion=1.11.0-20230831-224400-d2491ed

# Stdlib Level 08
Expand Down

0 comments on commit dc68a98

Please sign in to comment.