This package 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.
The graphql:Listener
is used to listening to a given IP/Port. To create a graphql:Listener
, an http:Listener
or a port number can be used.
graphql:Listener
1import ballerina/graphql;23listener graphql:Listener graphqlListener = new(4000);
graphql:Listener
using an http:Listener
1import ballerina/graphql;2import ballerina/http;34listener http:Listener httpListener = check new(4000);5listener graphql:Listener graphqlListener = new(httpListener);
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 ballerina/graphql;23listener graphql:Listener graphqlListener = new (4000, timeout = 10, secureSocket = { key: { path: <KEYSTORE_PATH>, password: <PASSWORD>}});
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 path of the GraphQL service endpoint can be provided via the service path of the GraphQL service. The endpoint of the following Ballerina GraphQL service will be /graphql
.
1import ballerina/graphql;23service /graphql on new graphql:Listener(4000) {4 // ...5}
The GraphQL service endpoint URL will be <host>:<port>/graphql
.
Alternatively, a Ballerina graphql service can not have a path, in which case the endpoint will be the host URL and the port as the following example.
1import ballerina/graphql;23service on new graphql:Listener(4000) {4 // ...5}
The GraphQL service endpoint URL will be <host>:<port>
The resource
functions inside the GraphQL service can represent the resolvers of the Query
root type.
When a resource
function is defined inside a GraphQL service with the get
accessor, the generated schema will have a Query
root type and the resource
function will be a field of the Query
object.
Note: A GraphQL service must have at least one resource function defined. Otherwise, it will result in a compilation error.
The accessor of the resource
function should always be get
for a field to be considered as a Query
field. 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;23service 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}
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 example, consider the following service that has a 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;23public type Person record {|4 string name;5 int age;6 string city;7|};89service /graphql on new graphql:Listener(4000) {10 private Person profile;1112 function init() {13 self.profile = { name: "Walter White", age: 50, city: "Albuquerque" };14 }1516 resource function get profile() returns Person {17 return self.profile;18 }1920 remote function updateName(string name) returns Person {21 self.profile.name = name;22 return self.profile;23 }2425 remote function updateCity(string city) returns Person {26 self.profile.city = city;27 return self.profile;28 }29}
This will generate the following schema:
1type Query {2 profile: Person!3}45type Mutation {6 updateName(name: String!): Person!7 updateCity(city: String!): Person!8}910type 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 oneresource
function defined.
This can be mutated using the following document.
1mutation updatePerson {2 updateName(name: "Mr. Lambert") {3 ... ProfileFragment4 }5 updateCity(city: "New Hampshire") {6 ... ProfileFragment7 }8}910fragment ProfileFragment on Person {11 name12 city13}
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.
The subscription type can be used to continuously fetch the data from a GraphQL service.
The resource
functions inside the GraphQL service with the subscribe
accessor can represent the resolvers of the Subscription
root type.
When a resource
function is defined inside a GraphQL service with the subscribe
accessor, the generated schema will have a Subscription
root type and the resource
function will be a field of the Subscription
object.
The accessor of the resource
function should always be subscribe
for a field to be considered as a Subscription
field. 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.
The resource
functions that belongs to Subscription
type must return a stream of any
type. Any other return type will result in a compilation error.
The return type
1import ballerina/graphql;23service graphql:Service /graphql on new graphql:Listener(4000) {4 resource function subscribe messages() returns stream<string> {5 return ["Walter", "Jesse", "Mike"].toStream();6 }7}
The above can be queried using the GraphQL document below:
1subscription {2 messages3}
When a subscription type is defined, a websocket service will be created to call the subscription. The above service will create the service as follows:
1ws://<host>:4000/graphql
This can be accessed using a websocket client. When the returned stream has a new entry, it will be broadcasted to the subscribers.
Additional configurations of a Ballerina GraphQL service can be provided using the graphql:ServiceConfig
.
These configurations include security-related configurations for the GraphQL service.
A GraphQL service can be secured by setting the auth
field in the graphql:ServiceConfig
. Ballerina GraphQL services support 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 definition23}
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;23@graphql:ServiceConfig {4 maxQueryDepth: 25}6service graphql:Service /graphql on new graphql:Listener(9090) {7 // Service definition8}
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 books7 }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": 19 }10 ]11 }12 ]13}
This field is used to initialize the graphql:Context
object. Usage of the graphql:Context
will be described in a separate section.
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 always be 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.
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.
1import ballerina/graphql;2import ballerina/http;34@graphql:ServiceConfig {5 contextInit: isolated function(http:RequestContext requestContext, http:Request request) returns graphql:Context|error {6 graphql:Context context = new;7 context.set("<key>", <value>);8 return context;9 }10}11service on new graphql:Listener(4000) {12 // ...13}
1import ballerina/graphql;2import ballerina/http;34isolated function initContext(http:RequestContext requestContext, http:Request request) returns graphql:Context|error {5 graphql:Context context = new;6 context.set("<key>", <value>);7 return context;8}910@graphql:ServiceConfig {11 contextInit: initContext12}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.
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 to any remote
function, or a resource
function inside a service object used as a GraphQL object type.
There are two methods to retrieve attributes from the graphql:Context
.
get()
functionThis 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()
functionThis 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}
The Ballerina GraphQL resources can return the following types:
The following Ballerina types are considered as Scalar types:
int
string
boolean
float
1resource function get greeting() returns string {2 return "Hello, World";3}
This can be queried using the following document:
1{2 greeting3}
Result:
1{2 "data": {3 "greeting": "Hello, World"4 }5}
When a resource
or a remote
function returns an enum
value, it will be mapped to a GraphQL ENUM
type.
1import ballerina/graphql;23public enum Color {4 RED,5 GREEN,6 BLUE7}89service on new graphql:Listener(4000) {10 resource function get color(int code) returns Color {11 // ...12 }13}
The above service will generate the following GraphQL schema.
1type Query {2 color: Color!3}45enum Color {6 RED7 GREEN8 BLUE9}
When a resource
function 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|};56resource 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}45type Person {6 name: String!7 age: Int!8}
This can be queried using the following document:
1{2 profile {3 name4 age5 }6}
Result:
1{2 "data": {3 "profile": {4 "name": "Walter White",5 "age": 516 }7 }8}
Each field can be queried separately as shown in the following document:
1{2 profile {3 name4 }5}
Result:
1{2 "data": {3 "profile": {4 "name": "Walter White"5 }6 }7}
When a resource
function returns a service type, 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;23service graphql:Service /graphql on new graphql:Listener(4000) {4 resource function get profile() returns Person {5 return new("Walter White", 51);6 }7}89service class Person {10 private string name;11 private int age;1213 public function init(string name, int age) {14 self.name = name;15 self.age = age;16 }1718 resource function get name() returns string {19 return self.name;20 }2122 resource function get age() returns int {23 return self.age;24 }25}
This will generate the following schema:
1type Query {2 profile: Person!3}45type Person {6 name: String!7 age: Int!8}
This can be queried using the following document:
1query getProfile {2 profile {3 name4 }5}
The above will result in the following JSON:
1{2 "data": {3 "profile": {4 "name": "Walter White"5 }6 }7}
A GraphQL resource
function can return an array of the types mentioned above. When a resource
function is returning an array, the result will be a JSON array.
1public type Person record {|2 string name;3 int age;4|};56resource 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}45type Person {6 name: String!7 age: Int!8}
This can be queried using the following document:
1{2 people {3 name4 }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.
A Ballerina GraphQL resource
function 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|};56resource 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: Person3}45type Person {6 name: String!7 age: Int!8}
This can be queried using the following document:
1{2 profile(id: 1) {3 name4 }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 name4 }5}
This will be the result:
1{2 "data": {3 "profile": null4 }5}
The Ballerina GraphQL service can return a union of distinct service types. This will be mapped to a GraphQL UNION
type.
Note: Since Ballerina supports 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 (
returns T1|T2|T3
), the type name will beT1_T2_T3
.
1import ballerina/graphql;23public type StudentOrTeacher Student|Teacher;45service /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}1415distinct service class Student {16 private int id;17 private string name;1819 public function init(int id, string name) {20 self.id = id;21 self.name = name;22 }2324 resource function get id() returns int {25 return self.id;26 }2728 resource function get name() returns string {29 return self.name;30 }31}3233distinct service class Teacher {34 private int id;35 private string name;36 private string subject;3738 public function init(int id, string name, string subject) {39 self.id = id;40 self.name = name;41 self.subject = subject;42 }4344 resource function get id() returns int {45 return self.id;46 }4748 resource function get name() returns string {49 return self.name;50 }5152 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}45type Student {6 id: Int!7 name: String!8}910type Teacher {11 id: Int!12 name: String!13 subject: String!14}1516union StudentOrTeacher Student|Teacher
This can be queried using the following document:
1query {2 profile(purity: 75) {3 ... on Student {4 name5 }6 ... on Teacher {7 name8 subject9 }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 name5 }6 ... on Teacher {7 name8 subject9 }10 }11}
The result will be:
1{2 "data": {3 "profile": {4 "name": "Walter White",5 "subject": "Chemistry"6 }7 }8}
A Ballerina GraphQL resource
function can return an error
with the union of the types mentioned above.
Note: A
resource
or aremote
function cannot return only anerror
, any subtype of anerror
, or, anerror?
, which will result in a compilation error.
1public type Person record {|2 string name;3 int age;4|};56resource 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 name4 }5}
Result:
1{2 "errors": [3 {4 "message": "Invalid ID provided: 5",5 "locations": [6 {7 "line": 2,8 "column": 49 }10 ]11 }12 ]13}
A resource
function 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;23service graphql:Service /graphql on new graphq:Listener(4000) {4 resource function profile/name/first() returns string {5 return "Walter";6 }78 resource function profile/name/last() returns string {9 return "White"10 }1112 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}45type profile {6 name: name!7 age: Int!8}910type name {11 first: String!12 last: String!13}
Note: The field name and the type names are equal.
To report bugs, request new features, start new discussions, view project boards, etc., go to the Ballerina standard library parent repository.
ballerina/graphql
ballerina/graphql.dataloader
ballerina/graphql.subgraph
sha-256:
01e6d6aa76efb5404c96e7e93137165cf0982d6d8f9f7da4ef57c05a4fa68705
License: Apache-2.0
Created date: 18 September,2023
Platform: java17
Ballerina version: 2201.8.0
Verified with GraalVM: Yes
gql
network
query
service