rfcs/rest-openapi-integration.md
| Version | MVP | Functionality | Notes / Comments |
|---|---|---|---|
| V1 | Bare minimum useful functionality | List of endpoints with methods and comments | Completed |
| V2 | Bare minimum useful functionality | All request types / arguments documented | Completed |
| V3 | User will get the Swagger JSON blob that can be used at Swagger editor to get the swagger UI | All response types documented | Completed |
| V4 | The Swagger UI will support authorization via header | Support role based authorization system | Completed |
| V5 | The swagger UI can be accessed using the Hasura console | UI in console | (We may not implement this) |
OpenAPI provides a common framework for designers, developers, testers, and devops to build and maintain REST APIs. We aim to generate a specification page that documents the REST endpoints and provides a tool to execute them with appropriate parameters. The consumers can understand and connect with the API remote services using the UI.
This document will specify the integration of REST endpoints that Hasura uses/generates with the OpenAPI specifications and generate json accepted by OpenAPI standards to design, build, document, test and standardize REST API endpoints.
To tackle this, we will:
Generate the OpenAPI specification when the user hits the endpoint (api/swagger/json). This will dynamically generate the JSON blob based on the current role of the user. We need to create the OpenAPI specification JSON blob using the REST endpoint specification. The REST endpoint specification can be extracted from either the schema cache, schema cache would be a good option for this (precomputing when dependencies change, more information about any issues upfront rather than at query-time).
Following is the roadmap for extracting the OpenAPI specification from the schema cache:
[
{
"tag": "PathLiteral",
"contents": "myAPIpost"
},
{
"_trieData": {
"POST": [
{
"definition": {
"query": "mutation MyMutation($col_1: String = \"\", $col_2: String = \"\", $id: Int = 10) {\n insert_table_1(objects: {col_1: $col_1, col_2: $col_2, id: $id}) {\n affected_rows\n }\n}"
},
"url": "myAPIpost",
"methods": [
"POST"
],
"name": "newAPI",
"comment": null
}
]
},
"_trieMap": []
}
]
Expose the Swagger JSON directly for the user. We can add a button in the console where user can download the JSON and then they can use Swagger editor of their choice (eg. https://editor.swagger.io/).
Serve the Swagger UI at an endpoint (/api/swagger) using a CDN (eg. https://www.jsdelivr.com/package/npm/swagger-ui-dist).
Create our own custom Swagger UI using the OpenAPI JSON (refer to https://github.com/swagger-api/swagger-ui).
| Option | Pros | Cons |
|---|---|---|
| 1 | User will have full control over the UI | Not very user friendly |
| 2 | User will get the familiar Swagger UI | Will expose our system to security threats from external CDN |
| 3 | We will have full control over the UI, scalable, we can re-use telemetry system, possibly we could re-use the locally stored headers from GraphiQL | Have to create our own UI , bundle size might increase (code splitting might help) |
We can also embed the Swagger UI (editor.swagger.io) in an
iFrame
We can serve the static Swagger files without a CDN and use them to render the OpenAPI JSON directly.
An example of serving Swagger UI using CDN (Option 2)
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<script src='https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.22.1/swagger-ui-standalone-preset.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.22.1/swagger-ui-bundle.js'></script>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.22.1/swagger-ui.css' />
<title>Swagger</title>
</head>
<body>
<div id='swagger-ui'></div>
<script>
window.onload = function() {
SwaggerUIBundle({
url: '/api/swagger/json',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
layout: 'StandaloneLayout'
})
}
</script>
</body>
</html>
Currently we are simply exposing the JSON as per options 1.
The query parser that is being used resolves the query, a subsequent method statically analyzes the type of each field present in the query by leveraging the introspection schema.