Data Sharing Using Graphql: Chaiporn Jaikaeo Department of Computer Engineering Kasetsart University

Download as pdf or txt
Download as pdf or txt
You are on page 1of 22

Data Sharing using GraphQL

01219335 Data Acquisition and Integration

Chaiporn Jaikaeo
Department of Computer Engineering
Kasetsart University

Revised 2020-11-04
Outline
• Limitations of REST APIs
• GraphQL concept
• GraphQL wrapper for OpenAPI

2
Downsides of REST APIs
• Multiple endpoints exposed to clients
• Multiple round trips by clients
• Under/over-fetching of data
GET /basins/3/stations
[{'id':12},{'id':45}]

GET /stations/12
{'id':12,'name':'a','lat':13.8,'lon':101.5}

GET /stations/45
Client {'id’:45,'name':'b','lat’:10.5,'lon’:103.9}
Server

3
GraphQL
• Graph Query Language, developed by Facebook
• Data are modeled as a graph
• APIs are organized around types and fields,
not endpoints
• Clients see APIs similar to Object-Oriented Programming
• Since GraphQL only defines the interface, any backend
can be used
GraphQL
REST GraphQL RPC REST RPC
Business Logic Layer Business Logic Layer
Persistence Layer Persistence Layer
(e.g., databases) (e.g., databases)

4
GraphQL Schema
• Model data as a graph by defining a schema, containing different
types of nodes and how they link to one another
type Query { { {
basin(basinId: Int!): Basin basin(basinId: 3) { "data": {
root type basins: [Basin] basinId "basin": {
} name "basinId": 3,
stations { "name": "Ping",
type Basin { stationId "stations": [
basinId: Int name {
area: Float } "stationId": 327301,
name: String } "name": "MAE JO AGROMET."
stations: [Station] } },
} {
Query "stationId": 327501,
type Station { "name": "CHIANG MAI"
stationId: Int }
name: String ]
basin: Basin }
lat: Float }
lon: Float }
}
Result
Schema
(can also be expressed in other languages)

5
GraphQL and OpenAPI
• OpenAPI is designed for RESTful APIs
• OpenAPI-to-GraphQL is a tool developed by IBM to create
a GraphQL wrapper around existing OpenAPI-based APIs
◦ Shown to work with most existing OpenAPI specifications
(though not 100%)

https://github.com/IBM/openapi-to-graphql/blob/master/docs/tutorials/watson.md 6
Example: Rainfalls GraphQL API
• We will create a simple GraphQL wrapper around the REST
API using OpenAPI-to-GraphQL
• Clone/download example code from
◦ https://gitlab.com/cjaikaeo/rain-graphql-daq-2020f
• Root path is /rain-api/v2
◦ Slightly modified from the previous v1 spec
• All REST endpoints have already been implemented

7
Running the Example (1)
• Inside the project folder, create and activate a virtual
environment
python3.9 -m venv env
. env/bin/activate # macOS and Linux
env\Scripts\activate.bat # Windows

• Generate code from the OAS


java -jar openapi-generator-cli-4.3.1.jar generate \
-i openapi/rain-api.yaml -o autogen -g python-flask

May type them all in one line For Windows’ command prompt, use the ^ character
without the \ character instead of \ to continue on the second line

• Install required libraries


pip install -r requirements.txt

8
Running the Example (2)
• Create config.py from config.py.example
• Start the REST API server
python app.py

◦ Optionally test the API at http://localhost:8080/rain-api/v2/ui


• Start openapi-to-graphql in another terminal
openapi-to-graphql -u http://localhost:8080/rain-api/v2 openapi/rain-api.yaml

• Open the page http://localhost:3000/graphql


◦ The browser should display GraphQL window
Notes: Use different ports if default ports are not available

9
GraphiQL
• A graphical interactive in-browser GraphQL IDE

Query result

GraphQL query
Schema
documentation

10
OpenAPI-to-GraphQL: Schema Mapping
• GraphQL interface is created around data definitions
(i.e., schema), not endpoints
paths: {
/basins/{basinId}: basin(basinId: 3) {
parameters: name
- name: basinId area
in: path }
required: true }
schema:
type : integer
components:
get:
operationId: controller.get_basin_details schemas:
responses: Basin:
200: type: object
properties:
description: Success
basinId:
content:
application/json: type: integer
schema: name:
$ref: '#/components/schemas/Basin' type: string
area:
type: number

11
Basic Query Examples
{
basins {
name Get all basins’ names
}
}

{
basins {
name
area Get all basins’ names and their areas
}
}

{
basin(basinId:3) {
name Get the name and area of the basin
}
area
whose ID is 3
}

12
OpenAPI-to-GraphQL: Links
• Links (new in OpenAPI 3.0) are used to create nested data structures
paths: {
/basins/{basinId}: basin(basinId: 3) {
parameters:
name
- name: basinId
in: path area
required: true stations {
schema: name
type : integer lat
get: lon
operationId: controller.get_basin_details
}
responses:
200: }
description: Success }
content:
application/json:
schema:
$ref: '#/components/schemas/Basin'
links:
stations:
operationId: controller.get_stations_in_basin
parameters:
basinId: $response.body#/basinId

13
OpenAPI-to-GraphQL: Nest Queries
• A nested query gets translated into multiple REST API requests
• Responses are then combined into a single GraphQL response
{ {
basin(basinId: 3) { "data": {
"basin": {
name
area
1 GET /rain-api/v2/basins/3 "name": "Ping",
"area": 37211.9,
stations { "stations": [
name {
lat "name": "MAE JO AGROMET.",
2 GET /rain-api/v2/stationsInBasin/3 "lat": 18.7833,
lon
"lon": 98.9833
}
},
} {
} "name": "CHIANG MAI",
"lat": 18.79,
"lon": 98.9769
}
]
}
}
}

14
Example: Rainfalls API with Links
• The folder openapi contains rain-api-with-links.yaml
◦ Similar to rain-api.yaml, with links added

• Restart openapi-to-graphql with the new yaml file


paths:
/basins/{basinId}:
...
get:
... Points to another endpoint
links:
stations:
operationId: controller.get_stations_in_basin
parameters: And passes this
basinId: $response.body#/basinId
annualRainfall:
parameter value
operationId: controller.get_basin_annual_rainfall
parameters:
basinId: $response.body#/basinId
allAnnualRainfalls:
operationId: controller.get_basin_all_annual_rainfalls
parameters:
basinId: $response.body#/basinId

15
Running the Example with Links
• Start the REST API server
python app-with-links.py
◦ Optionally test the API at http://localhost:8080/rain-api/v2/ui

• Start openapi-to-graphql in another terminal


openapi-to-graphql \
-u http://localhost:8080/rain-api/v2 \
openapi/rain-api-with-links.yaml

Notes: you may put all the above in a single line without \

16
Nested Query Examples
{
basins { Get all basins’ names and the names of
name
stations { name lat lon } all stations inside, along with their
}
}
latitude and longitude

{
basins {
name Get all basins’ names and their annual
}
annualRainfall(year:2000)
rainfalls in the year 2000
}

{ Get all basins’ names and their


basins {
name annual rainfalls in the years 1993
drought:annualRainfall(year:1993)
flood:annualRainfall(year:2011)
and 2011
} • Also define aliases to the results as
} ‘drought’ and ‘flood’, respectively

17
Exercise: Monthly Rainfalls
• Add a new link to in openapi/rain-api-with-links.yaml so
that average monthly rainfalls is nested inside Basin objects
◦ Name the nested field avgMonthlyRainfalls
◦ Link to the endpoint /rain-api/v2/basins/{basinId}/monthlyAverage

• Your new spec must be able to run this query through OpenAPI-to-
GraphQL
{
basins {
name
avgMonthlyRainfalls {
month
amount
}
}
}

18
Conclusion
• GraphQL is a query language designed to provide a single
API endpoint for retrieving all and only needed data in a
single request
• Nested data structures are used to avoid multiple requests
• OpenAPI-to-GraphQL is a tool for creating a simple
GraphQL wrapper around an existing OpenAPI specification
• To support nested queries, OpenAPI’s links are heavily
utilized by OpenAPI-to-GraphQL

19
Further Reading
• Links in OpenAPI 3
◦ https://swagger.io/docs/specification/links/
• GraphQL - A query language for your API
◦ https://graphql.org/
• IBM’s OpenAPI-to-GraphQL Project
◦ https://developer.ibm.com/open/projects/openapi-to-graphql/
◦ https://github.com/IBM/openapi-to-
graphql/tree/master/packages/openapi-to-graphql
• Erik Wittern, Alan Cha, and Jim A. Laredo. Generating GraphQL-
Wrappers for REST(-like) APIs
◦ https://arxiv.org/pdf/1809.08319.pdf

20
Assignment 9.1: All-years Annual Rainfalls
• Modify openapi/rain-api-with-links.yaml to create a new REST endpoint
that returns a list of annual rainfalls in all available years for a specified basin
◦ Endpoint URL: /rain-api/v2/basins/{basinId}/allAnnualRainfalls
◦ Return: list of AnnualRainfall objects, already defined in the schemas section

• Implement the new endpoint in controller.py


• Allow access to the above via GraphQL queries by the name
allAnnualRainfalls nested inside Basin objects

21
Assignment 9.1's Expected Results
• Result from /rain-api/v2/basins/2/allAnnualRainfalls
[
{
"amount": 1055.12,
"year": 1987
},
{
"amount": 1309.64, {
"year": 1988 "data": {
}, "basin": {
: "name": "Ping",
] "allAnnualRainfalls": [
{
"year": 1987,
• Result from GraphQL query "amount": 1055.12
},
{ {
basin(basinId:3) { "year": 1988,
name "amount": 1309.64
allAnnualRainfalls { },
year :
amount ]
} }
} }
} }

22

You might also like