ballerina/graphql

Overview

This module provides APIs for connecting and interacting with GraphQL endpoints.

GraphQL is an open-source data query and manipulation language for APIs. GraphQL allows clients to define the structure of the data required and the same structure of the data is returned from the server preventing the returning of excessively large amounts of data.

The Ballerina GraphQL implementation is using HTTP as the underlying protocol.

Listener

The graphql:Listener is used to listen to a given IP/Port. To create a graphql:Listener, an http:Listener or a port number can be used.

Create a Standalone graphql:Listener

1import ballerina/graphql;
2
3listener graphql:Listener graphqlListener = new(4000);

Create a graphql:Listener Using an http:Listener

1import ballerina/graphql;
2import ballerina/http;
3
4listener http:Listener httpListener = check new(4000);
5listener graphql:Listener graphqlListener = new(httpListener);

Additional Configurations

When initializing the Ballerina GraphQL listener, a set of additional configurations can be provided to configure the listener including security and resiliency settings. The configurations that can be passed for this are defined in the graphql:ListenerConfiguration record.

1import ballrina/graphql;
2
3listener graphql:Listener graphqlListener = new (4000, timeout = 10, secureSocket = { key: { path: <KEYSTORE_PATH>, password: <PASSWORD>}});

Service

The Ballerina GraphQL service represents the GraphQL schema. When a service is attached to a graphql:Listener, a GraphQL schema will be auto-generated.

The GraphQL services are exposed through a single endpoint. The URL of the GraphQL service can be provided when defining the service. (Check the next example, in which the URL is /graphql).

Query Type

The resource functions inside the service represent the resolvers of the Query root type.

When a resource function is defined inside a GraphQL service, the generated schema will have a Query root type and the resource function will be a field of the Query object.

The accessor of the resource function should always be get. The resource function name will become the name of the particular field in the GraphQL schema. The return type of the resource function will be the type of the corresponding field.

1import ballerina/graphql;
2
3service graphql:Service /graphql on new graphql:Listener(4000) {
4 resource function get greeting(string name) returns string {
5 return "Hello, " + name;
6 }
7}

The above can be queried using the GraphQL document below:

1{
2 greeting(name: "John")
3}

The result will be the following JSON.

1{
2 "data": {
3 "greeting": "Hello, John"
4 }
5}

Mutation Type

The remote functions inside the GraphQL service represent the resolvers of the Mutation root type.

When a remote function is defined inside a GraphQL service, the schema will have a Mutation operation and the remote function will be a field of the Mutation object.

For an example, consider the following service that has a Person record named person. It has a Query field named profile, which returns the person record. It also has two remote functions named updateName and updateCity, which are used as mutations.

1import ballerina/graphql;
2
3public type Person record {|
4 string name;
5 int age;
6 string city;
7|};
8
9service /graphql on new graphql:Listener(4000) {
10 private Person profile;
11
12 function init() {
13 self.profile = { name: "Walter White", age: 50, city: "Albuquerque" };
14 }
15
16 resource function get profile() returns Person {
17 return self.profile;
18 }
19
20 remote function updateName(string name) returns Person {
21 self.profile.name = name;
22 return self.profile;
23 }
24
25 remote function updateCity(string city) reutns Person {
26 self.profile.city = city;
27 return self.profile;
28 }
29}

This will generate the following schema:

1type Query {
2 profile: Person!
3}
4
5type Mutation {
6 updateName(name: String!): Person!
7 updateCity(city: String!): Person!
8}
9
10type Person {
11 name: String!
12 age: Int!
13 city: String!
14}

Note: A GraphQL schema must have a root Query type. Therefore, a Ballerina GraphQL service must have at least one resource function defined.

This can be mutated using the following document.

1mutation updatePerson {
2 updateName(name: "Mr. Lambert") {
3 ... ProfileFragment
4 }
5 updateCity(city: "New Hampshire") {
6 ... ProfileFragment
7 }
8}
9
10fragment ProfileFragment on Profile {
11 name
12 city
13}

Note: This document uses two mutations and each mutation requests the same fields from the service using a fragment (ProfileFragment).

Result:

1{
2 "data": {
3 "updateName": {
4 "name": "Mr. Lambert",
5 "city": "Albuquerque"
6 },
7 "updateCity": {
8 "name": "Mr. Lambert",
9 "city": "New Hampshire"
10 }
11 }
12}

See how the result changes the Person record. The first mutation changes only the name and it populates the result of the updateName field. Then, it will execute the updateCity operation and populate the result. This is because the execution of the mutation operations will be done serially in the same order as they are specified in the document.

Additional Configurations

Additional configurations of a Ballerina GraphQL service can be provided using the graphql:ServiceConfig. These configurations include security-related configurations for the GraphQL service.

Security Configurations

A GraphQL service can be secured by setting auth field in the graphql:ServiceConfig. Ballerina GraphQL services supports Basic Authentication, JWT Authentication and OAuth2 Authentication.

1@graphql:SeviceConfig {
2 auth: [
3 {
4 oauth2IntrospectionConfig: {
5 url: <auth_introspection_url>,
6 tokenTypeHint: <access_token>,
7 scopeKey: <scope_key>,
8 clientConfig: {
9 secureSocket: {
10 cert: {
11 path: <truststore_path>,
12 password: <password>
13 }
14 }
15 }
16 },
17 scopes: [<comma_separated_list_of_scopes>]
18 }
19 ]
20}
21service graphql:Service /graphql on new graphql:Listener(4000) {
22 // Service definition
23}
Maximum Query Depth

When a maximum query depth is provided, all the queries exceeding that limit will be rejected at the validation phase and will not be executed.

1import ballerina/graphql;
2
3@graphql:ServiceConfig {
4 maxQueryDepth: 2
5}
6service graphql:Service /graphql on new graphql:Listener(9090) {
7 // Service definition
8}

The above service only accepts queries of less than 2 levels. For an example, consider the following document:

1query getData {
2 book {
3 author {
4 books {
5 author {
6 books
7 }
8 }
9 }
10 }
11}

The result for the above query is the following JSON:

1{
2 "errors": [
3 {
4 "message": "Query \"getData\" has depth of 5, which exceeds max depth of 2",
5 "locations": [
6 {
7 "line": 1,
8 "column": 1
9 }
10 ]
11 }
12 ]
13}
Context Init

This field is used to initialize the graphql:Context object. Usage of the graphql:Context will be described in a separate section.

Context

The graphql:Context can be used to pass meta-information among the graphql resolver (resource/remote) functions. It will be created per each request, with a defined set of attributes. Attributes can be stored in the graphql:Context object using key, value pairs. The key should be always a string. The type of the value is value:Cloneable|isolated object {}. This means the values can be any immutable type, readonly value, or an isolated object. These attributes can be set using a function, which can be given as a service configuration parameter.

Context Init

The graphql:Context can be initialized using a function. The function signature is as follows:

1isolated function (http:RequestContext requestContext, http:Request request) returns graphql:Context|error {}

The values from the http:RequestContext and the http:Request can be set as attributes of the graphql:Context since they are passed as arguments for this function. Then the function should be provided as a graphql:ServiceConfig parameter.

Following are examples for providing the context init function.

Providing the Init Function Directly
1import ballerina/graphql;
2import ballerina/http;
3
4@graphql:ServiceConfig {
5 contextInit: isolated function(http:RequestContext requestContext, http:Request request) returns graphql:Context|error {
6 graphql:Context context = new;
7 check context.add("<key>", <value>);
8 return context;
9 }
10}
11service on new graphql:Listener(4000) {
12 // ...
13}
Providing the Init Function as a Function Pointer
1import ballerina/graphql;
2import ballerina/http;
3
4isolated function initContext(http:RequestContext requestContext, http:Request request) returns graphql:Context|error {
5 graphql:Context context = new;
6 check context.add("<key>", <value>);
7 return context;
8}
9
10@graphql:ServiceConfig {
11 contextInit: initContext
12}
13service on new graphql:Listener(4000) {
14 // ...
15}

Note: Even if the context init function is not provided, a default, empty context will be created per each request.

Using the Context in Resolver Functions

If the graphql:Context needs to be accessed, the resolver function has to add it as the first parameter of the function. Following is an example:

1service on new graphql:Listener(4000) {
2 resource function get greet(graphql:Context context) returns string {
3 return "Hello, World!";
4 }
5}

This is similar for any remote function, or a resource function inside a service object used as a GraphQL object type.

Retrieving Attributes from the Context

There are two methods to retrieve attributes from the graphql:Context.

get() Function

This will return the value of the attribute using the provided key. If the key does not exist, it will return a graphql:Error.

1resource function get greeting(graphql:Context context) returns string|error {
2 var username = check context.get("username");
3 if username is string {
4 return "Hello, " + username;
5 }
6 return "Hello, World!";
7}
remove() Function

This function will remove the attribute for a provided key, and return the value. If the key does not exist, it will return a graphql:Error.

Note: Even though this is supported, destructive-modification of the graphql:Context is discouraged. This is because these modifications may affect the parallel executions in queries.

1resource function get greeting(graphql:Context context) returns string|error {
2 var username = check context.remove("username");
3 if username is string {
4 return "Hello, " + username;
5 }
6 return "Hello, World!";
7}
8
9### Types
10The Ballerina GraphQL resources can return the following types:
11
12#### Return Types
13
14##### Scalar types
15The following Ballerina types are considered as Scalar types:
16
17* `int`
18* `string`
19* `boolean`
20* `float`
21* `decimal`
22* `enum`
23
24```ballerina
25resource function get greeting() returns string {
26 return "Hello, World";
27}

This can be queried using the following document:

1{
2 greeting
3}

Result:

1{
2 "data": {
3 "greeting": "Hello, World"
4 }
5}
Record types

When a resource is returning a record type, each field of the record can be queried separately. Each record type is mapped to a GraphQL OBJECT type and the fields of the record type are mapped to the fields of the OBJECT type.

1public type Person record {|
2 string name;
3 int age;
4|};
5
6resource function get profile() returns Person {
7 return { name: "Walter White", age: 51 };
8}

This will generate the following schema.

1type Query {
2 profile: Person!
3}
4
5type Person {
6 name: String!
7 age: Int!
8}

This can be queried using the following document:

1{
2 profile {
3 name
4 age
5 }
6}

Result:

1{
2 "data": {
3 "profile": {
4 "name": "Walter White",
5 "age": 51
6 }
7 }
8}

Each field can be queried separately as shown in the following document:

1{
2 profile {
3 name
4 }
5}

Result:

1{
2 "data": {
3 "profile": {
4 "name": "Walter White"
5 }
6 }
7}

Service Objects

When a resource function returns a service object, the service type is mapped to a GraphQL OBJECT type and the resource functions of the service type will be mapped as the fields of the OBJECT.

When a service type is returned from a graphql:Service, the returning service type should also follow the rules of the graphql:Service explained above.

1import ballerina/graphql;
2
3service graphql:Service /graphql on new graphql:Listener(4000) {
4 resource function get profile() returns Person {
5 return new("Walter White", 51);
6 }
7}
8
9service class Person {
10 private string name;
11 private int age;
12
13 public function init(string name, int age) {
14 self.name = name;
15 self.age = age;
16 }
17
18 resource function get name() returns string {
19 return self.name;
20 }
21
22 resource function get age() returns int {
23 return self.age;
24 }
25}

This will generate the following schema:

1type Query {
2 profile: Person!
3}
4
5type Person {
6 name: String!
7 age: Int!
8}

This can be queried using the following document:

1query getProfile {
2 profile {
3 name
4 }
5}

The above will result in the following JSON:

1{
2 "data": {
3 "profile": {
4 "name": "Walter White"
5 }
6 }
7}

Arrays

A GraphQL resource can return an array of the types mentioned above. When a resource is returning an array, the result will be a JSON array.

1public type Person record {|
2 string name;
3 int age;
4|};
5
6resource function get people() returns Person[] {
7 Person p1 = { name: "Walter White", age: 51 };
8 Person p2 = { name: "James Moriarty", age: 45 };
9 Person p3 = { name: "Tom Marvolo Riddle", age: 71 };
10 return [p1, p2, p3];
11}

This will generate the following schema:

1type Query {
2 profile: [Person!]!
3}
4
5type Person {
6 name: String!
7 age: Int!
8}

This can be queried using the following document:

1{
2 people {
3 name
4 }
5}

Result:

1{
2 "data": {
3 "people": [
4 {
5 "name": "Walter White"
6 },
7 {
8 "name": "James Moriarty"
9 },
10 {
11 "name": "Tom Marvolo Riddle"
12 }
13 ]
14 }
15}

Note: Each element in the array consists only of the required name field.

Optional Types

A Ballerina GraphQL resource can return an optional type. When the return value is (), the resulting field in the JSON will be null.

1public type Person record {|
2 string name;
3 int age;
4|};
5
6resource function get profile(int id) returns Person? {
7 if (id == 1) {
8 return { name: "Walter White", age: 51 };
9 }
10}

This will generate the following schema:

1type Query {
2 profile: Person
3}
4
5type Person {
6 name: String!
7 age: Int!
8}

This can be queried using the following document:

1{
2 profile(id: 1) {
3 name
4 }
5}

Result:

1{
2 "data": {
3 "profile": {
4 "name": "Walter White"
5 }
6 }
7}

If the following document is used:

1{
2 profile(id: 4) {
3 name
4 }
5}

This will be the result:

1{
2 "data": {
3 "profile": null
4 }
5}

Union Types

The Ballerina GraphQL service can return a union of distinct service types. This will be mapped to a GraphQL UNION type.

Since Ballerina supports the union types by nature, directly returning a union type is also allowed (but not recommended). The recommended way is to define a union type name separately and then use that type name as shown in the following example. If a union type is returned directly without providing a type name, the type name will be T1|T2|T3|...|Tn.

1import ballerina/graphql;
2
3public type StudentOrTeacher Student|Teacher;
4
5service /graphql on new graphql:Listener(4000) {
6 resource function get profile(int purity) returns StudentOrTeacher {
7 if (purity < 90) {
8 return new Student(1, "Jesse Pinkman");
9 } else {
10 return new Teacher(737, "Walter White", "Chemistry");
11 }
12 }
13}
14
15distinct service class Student {
16 private int id;
17 private string name;
18
19 public function init(int id, string name) {
20 self.id = id;
21 self.name = name;
22 }
23
24 resource function get id() returns int {
25 return self.id;
26 }
27
28 resource function get name() returns string {
29 return self.name;
30 }
31}
32
33distinct service class Teacher {
34 private int id;
35 private string name;
36 private string subject;
37
38 public function init(int id, string name, string subject) {
39 self.id = id;
40 self.name = name;
41 self.subject = subject;
42 }
43
44 resource function get id() returns int {
45 return self.id;
46 }
47
48 resource function get name() returns string {
49 return self.name;
50 }
51
52 resource function get subject() returns string {
53 return self.subject;
54 }
55}

This will generate the following schema:

1type Query {
2 profile(purity: Int!): StudentOrTeacher!
3}
4
5type Student {
6 id: Int!
7 name: String!
8}
9
10type Teacher {
11 id: Int!
12 name: String!
13 subject: String!
14}
15
16union StudentOrTeacher Student|Teacher

This can be queried using the following document:

1query {
2 profile(purity: 75) {
3 ... on Student {
4 name
5 }
6 ... on Teacher {
7 name
8 subject
9 }
10 }
11}

The result will be:

1{
2 "data": {
3 "profile": {
4 "name": "Jesse Pinkman"
5 }
6 }
7}

If the following document is used:

1query {
2 profile(purity: 99) {
3 ... on Student {
4 name
5 }
6 ... on Teacher {
7 name
8 subject
9 }
10 }
11}

The result will be:

1{
2 "data": {
3 "profile": {
4 "name": "Walter White",
5 "subject": "Chemistry"
6 }
7 }
8}

Errors (With union)

A Ballerina GraphQL resource can return an error with the union of the types mentioned above.

Note: A resource cannot return an error, any subtype of an error, or, an error?, which will result in a compilation error.

1public type Person record {|
2 string name;
3 int age;
4|};
5
6resource function get profile(int id) returns Person|error {
7 if (id == 1) {
8 return { name: "Walter White", age: 51 };
9 } else {
10 return error(string `Invalid ID provided: ${id}`);
11 }
12}

This can be queried using the following document:

1{
2 profile(id: 5) {
3 name
4 }
5}

Result:

1{
2 "errors": [
3 {
4 "message": "Invalid ID provided: 5",
5 "locations": [
6 {
7 "line": 2,
8 "column": 4
9 }
10 ]
11 }
12 ]
13}

Hierarchical Resource Paths

A resource inside a GraphQL service can have hierarchical paths. When a hierarchical path is present, each level of the hierarchical path maps to the GraphQL field of the same name, and the type of that field will be mapped to an OBJECT type with the same name.

1import ballerina/graphql;
2
3service graphql:Service /graphql on new graphq:Listener(4000) {
4 resource function profile/name/first() returns string {
5 return "Walter";
6 }
7
8 resource function profile/name/last() returns string {
9 return "White"
10 }
11
12 resource function profile/age() returns int {
13 return 51;
14 }
15}

The above service will create the following schema:

1type Query {
2 profile: profile!
3}
4
5type profile {
6 name: name!
7 age: Int!
8}
9
10type name {
11 first: String!
12 last: String!
13}

Note: The field name, and the type names are equal.

Listeners

[1]

Listener

Represents a Graphql listener endpoint.

Classes

[1]

Context

Object Types

[1]

Service

Represents a GraphQL service.

Records

[13]

FileUserStoreConfig

Represents file user store configurations for Basic Auth authentication.

FileUserStoreConfigWithScopes

Represents the auth annotation for file user store configurations with scopes.

GraphqlServiceConfig

Provides a set of configurations for the GraphQL service.

JwtValidatorConfig

Represents JWT validator configurations for JWT authentication.

JwtValidatorConfigWithScopes

Represents the auth annotation for JWT validator configurations with scopes.

LdapUserStoreConfig

Represents LDAP user store configurations for Basic Auth authentication.

LdapUserStoreConfigWithScopes

Represents the auth annotation for LDAP user store configurations with scopes.

ListenerConfiguration

Provides a set of configurations for configure the underlying HTTP listener of the GraphQL listener.

ListenerHttp1Settings

Provides settings related to HTTP/1.x protocol, when using HTTP 1.x as the underlying protocol for the GraphQL service.

ListenerSecureSocket

Configures the SSL/TLS options to be used for the underlying HTTP service used in GraphQL service.

OAuth2IntrospectionConfig

Represents OAuth2 introspection server configurations for OAuth2 authentication.

OAuth2IntrospectionConfigWithScopes

Represents the auth annotation for OAuth2 introspection server configurations with scopes.

RequestLimitConfigs

Provides inbound request URI, total header and entity body size threshold configurations.

Annotations

[1]

ServiceConfig

The annotation to configure a GraphQL service.

Types

[3]

ContextInit

Function type for initializing the graphql:Context object.

ListenerAuthConfig

Defines the authentication configurations for the GraphQL listener.

Scalar

Represents the Scalar types supported by the Ballerina GraphQL module.

Errors

[1]

Error

Represents any error related to the Ballerina GraphQL module.