HTTP API Gateway Specification
An API Gateway specification is a document that tells Bondy how to route incoming HTTP requests to your WAMP APIs or to external HTTP/REST APIs.
Overview
An API Gateway Specification document is a JSON data structure that declaratively defines an HTTP/REST API and how Bondy should handle each HTTP Request e.g. by converting it into a WAMP operation or forwarding it to an upstream (external) HTTP/REST API. This includes capabilities for data transformation.
A declarative Finite State Machine (FSM)
In effect, an API Gateway Specification is a declarative definition of an API Gateway Finite State Machine that exposes an HTTP/REST API and converts its nouns and verbs to either WAMP or other HTTP/REST actions.
With this approach you can create a whole HTTP/REST API from scratch without any coding.
The API Gateway Specification document has a structure represented by the following object tree:
- API Object
- Version Object 1
- Path Object 1
HTTP METHOD
- ... other HTTP methods
- ... other path objects
- Path Object 1
- ... other version objects
- Version Object 1
The following diagram shows the object tree in detail, including all properties and types.
The properties of the objects in the object tree can contain static values and/or dynamically evaluated values via expressions that are resolved against the HTTP request data at runtime.
FSM State
The API Context is the state of the API Gateway FSM. It is is incrementally an recursively constructed.
So an API Gateway Specification is the basis of an API Context but also it is evaluated against it, primarily because the context will contain the Request Object at runtime.
The key to the definition of an API Gateway Specification is understanting the API Context, since defining a specification implies writing expressions that target (read and/or update) the context.
On the Open API standard
Open API (formerly Swagger) defines a standard on how HTTP APIs are described, not its implementation. An API Gateway Specification describes and API and the behaviour of the Gateway, that is, it also defines its implementation in terms of the actions that the Gateway need to perform.
A Future version of the API Gateway will aligning with Open API. In addition, the API Gateway implementation will be able to produce and serve an Open API specification of the APIs defined in Bondy.
API Context
The API context is a map data structure created by the API Gateway. At runtime, it contains the HTTP Request data as well as the results of parsing and evaluating the definitions and expressions defined in an API Gateway Specification.
The context contains the following keys:
request
RequestObjectThe contents (data and metadata) of the HTTP request being evaluated. The request data is available at runtime, so any expression defined during design time will result in a promise
that will be evaluated to a value when handling the HTTP request.
security
SecurityObjectAn instance of the Security Object. Its initial value comes from the API Object defaults.security
or variables.security
property. It is then possibly overridden recursively during evaluation by each [Path Object]#(path-object) definition.
action
objectAt runtime, it will contain the result or error of the action performed by the API Gateway during the handling of an HTTP request.
variables
mapA mapping of arbitrary variable names to values or expressions. Each entry in this map is obtained during the parsing of the API Object tree. At each level of the tree this property will merge in the values of the target object's variables
property, so children nodes can access the entries defined in the ancestors, override them and/or add new variables to the context.
defaults
mapA mapping of object properties to their default values. Each entry in this map is obtained during the parsing of the API Object tree. At each level of the tree this property will merge in the values of the target object's defaults
property, so children nodes can access the entries defined in the ancestors, override them and/or add new defaults to the context. Expressions in the defaults mapping can reference variables in the variables
property.
status_codes
mapA mapping of WAMP Error URIs to HTTP Status Codes, to be used when the Operation Object is a WAMP Action. The entries of this map are recursively updated during evaluation. At each level of the tree this property will merge in the values of the target object's status_codes
property, so children nodes can access the entries defined in the ancestors, override them and/or add new status codes to the context.
defaults
property.Request Object
The object represents the contents (data and metadata) of an HTTP request. At runtime, the API Gateway writes this object in the API Context request
property.
id
stringIMMUTABLEA unique tracing identifier for the request
method
stringIMMUTABLEThe HTTP method of the request. One of the following values:
delete
get
head
options
patch
post
put
scheme
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
peername
stringIMMUTABLEA string representation of the requester IP address and port number e.g. 127.0.0.1:54678
.
path
stringIMMUTABLEThe relative path of the request.
host
stringIMMUTABLEThe hostname of the request.
port
stringIMMUTABLEThe port number the request.
headers
() => mapIMMUTABLEA mapping of HTTP headers to their corresponding values.
language
stringIMMUTABLEThe requested language.
query_string
stringIMMUTABLEThe HTTP query string. See query_params
.
query_params
mapIMMUTABLEA map of query params to values. This is the result of parsing the request's query_string
.
Examples:
Request | Property Value |
---|---|
GET /users?region=us&type=individual |
{"region": "us", "type": "individual"} |
GET /users?x=100&y=200 |
{"x": "100", "y": "200"} |
bindings
mapIMMUTABLEA map of path variable bindings. Bindings occur when the Path Object contains patterns.
Examples: For the path specification /accounts/:acc_id/users/:user_id
Request | Property Value |
---|---|
GET /accounts/001/users/002 |
{"acc_id": "001", "user_id": "002"} |
body
anyIMMUTABLEThe body of the request. This is the result of decoding the HTTP body using the encoding determined by the Path Object accepts
property which defines the content-types allowed for POST
, PUT
and PATCH
.
body_length
integerIMMUTABLEThe length of the body in bytes.
You access the values in this object by writing expressions using the API Specification expression language.
Result Object
WAMP Result
The result for a WAMP Action.
This object will be accessible with the expression {{action.result}}
.
request_id
integerIMMUTABLEThe WAMP RESULT.id
.
args
array[any]IMMUTABLEThe WAMP RESULT.args
.
kwargs
() => mapIMMUTABLEThe WAMP RESULT.kwargs
.
HTTP Forward Result
Error Object
TBD
Expression Language
Most API Specification object properties support expressions using an embedded logic-less domain-specific language (internally called "Mops") for data transformation and dynamic configuration.
This same language is also used by the Broker Bridge Specification.
The expression language operates on the API Context and it works by expanding keys (or key paths) provided in a context object and adding or updating keys in the same context object.
Let's assume that we receive the following HTTP request:
curl -X "POST" "http://localhost:18081/accounts/" \
-H 'Content-Type: application/json; charset=utf-8' \
-H 'Accept: application/json; charset=utf-8' \
--data-binary '{
"id" : 12345
"sku" : "ZPK1972",
"price" : 13.99,
"customer": {
"first_name": "John",
"last_name": "Doe",
"email" : "john.doe@foo.com"
},
"ship_to": {
"first_name": "May",
"last_name": "Poppins",
"address" : "3 High Street",
"town" : "Guildford",
"county" : "Surrey",
"zip" : "GU1 1AF"
},
"bill_to": {
"first_name": "John",
"last_name": "Doe",
"address" : "13 Sandy Lane",
"town" : "Esher",
"county" : "Surrey",
"zip" : "KT11 2PQ"
}
}'
Let's explore a some example to demonstrate how you can use expression in Bondy's configuration objects to read data from the HTTP Request.
The following table shows some example expressions being evaluated against the API Context for the above HTTP Request.
Expression String | Evaluates To |
---|---|
{{request.method}} | POST |
{{request.body}} | {"id": 12345, "bill_to":...} |
{{request.body.sku}} | "ZPK1972" |
The sku number is {{request.body.sku}} | "The sku number is ZPK1972" |
{{request.body.price}} | 13.99 |
{{request.body.price |> integer}} | 13 |
{{request.body.price |> string}} | "13.99" |
{{request.body.customer.first_name}} | "John" |
{{request.body.customer.first_name}} {{request.body.customer.last_name}} | "John Doe" |
{{variables.foo}} | Returns the value of the foo variable |
{{defaults.status_codes}} | Returns the status codes map |
Learn more
Expressions also allow to set values in the context and use functions to manipulate the request data. Learn more about expressions in the API Specification Expressions reference section.
Specification Evaluation
Incremental Evaluation
The API Specification evaluation performed incrementally in two stages:
- During loading, validation and parsing. All API Specification expressions will be evaluated to either a (final) value or a
promise
. Promises occur when an expression depends directly or indirectly (transitive closure) on HTTP request data. This results in a context object. - During HTTP request handling at runtime. The context created in the first stage is updated with the HTTP request data and the API Specification is evaluated again using the updated context, yielding the actions to be performed with all promises evaluated to values (grounded).
Recursive Evaluation
The evaluation of the expressions in the API Gateway Specification is done by passing the API Context recursively throughout the specification object tree.
At each level of the tree children nodes can can use the values of the certain properties defined in the ancestor node (through expressions), override them and/or update the API Context.
API Object
The API object is the root of an API Specification. It contains one or more API Version objects.
id
stringREQUIREDIMMUTABLEA global unique identifier for this API.
host
stringREQUIREDIMMUTABLEA string used for matching the incoming HTTP request HOST header value. Hosts with and without a trailing dot are equivalent for routing. Similarly, hosts with and without a leading dot are also equivalent e.g. cowboy.example.org
, cowboy.example.org.
and .cowboy.example.org
are equivalent. A pattern using the keyword :
and wildcard _
can be used to match multiple domains e.g. mydomain.:_
will match mydomain.foo
and mydomain.bar
but not mydomain.foo.baz
.
realm_uri
stringREQUIREDIMMUTABLEThe realm this API will target. An API can only target a single realm.
name
stringIMMUTABLEA display name for the API.
variables
mapIMMUTABLEA mapping of arbitrary variable names to values or expressions. This variables can be referenced by expressions in the children objects of this object.
defaults
mapIMMUTABLEA mapping of attributes to their default values. This values are inherited by children objects as defaults when their value is unset.
status_codes
mapIMMUTABLEA mapping of WAMP Error URIs to HTTP Status Codes. This values are inherited by children objects as defaults when their value is unset.
versions
array[VersionObject]REQUIREDIMMUTABLEAn array of Version Object instances.
API Object example
{
"id": "example-api",
"name" : "Bonding in HTTP",
"host" : "_",
"realm_uri" : "com.example.public",
"meta": {"foo" : "bar"}
"variables" : {
"cors_headers": {
"access-control-allow-origin": "*",
"access-control-allow-credentials": "true",
"access-control-allow-methods": "GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE",
"access-control-allow-headers": "origin,x-requested-with,content-type,accept,authorization,accept-language",
"access-control-max-age": "86400"
}
},
"defaults" : {
"schemes" : ["http"]
},
"status_codes": {
"com.example.error.not_found": 404,
"com.example.error.unknown_error": 500,
"com.example.error.internal_error": 500
},
"versions" : [
...
]
}
Version Object
The Version Object represents a particular API version.
base_path
stringREQUIREDIMMUTABLEThe base path for this version of the API. This value will be used by the API Gateway to match incoming requests e.g. /v1.0
will match /v1.0/foo
but not /foo
. It is possible to have optional segments, anything between brackets is optional e.g. /[v1.0]
will match /v1.0/foo
and also /foo
.
paths
mapREQUIREDIMMUTABLEA mapping of paths to Path Objects. Paths are relative URL paths and can contain patterns and optional segments. The path /
is invalid while the path /ws
is reserved (used by Bondy for requesting Websocket connections).
is_active
booleanIMMUTABLEWhether the path is active.
true
is_deprecated
booleanIMMUTABLEWhether the path is deprecated i.e. the path will be removed in future versions of the API.
false
pool_size
integerIMMUTABLE200
info
objectvariables
mapIMMUTABLEA mapping of arbitrary variable names to values or expressions. This variables can be referenced by expressions in the children objects of this object. These values are merged with and thus override the ones inherited from the API Object variables
property.
defaults
mapIMMUTABLEA mapping of attributes to their default values. This values are inherited by children objects as defaults when their value is unset. These values are merged with and thus override the ones inherited from the API Object defaults
property.
languages
array[string]IMMUTABLEAn array of language code string.
Version Object example
TBD
Path Object
A path specification to be used as a value to a key in the paths
property of a Version Object.
summary
stringIMMUTABLEA short summary of the API.
description
stringIMMUTABLEA description of the API.
variables
mapIMMUTABLEA mapping of arbitrary variable names to values or expressions. This variables can be referenced by expressions in the children objects of this object. These values are merged with and thus override the ones inherited from the API Object variables property.
defaults
mapIMMUTABLEA mapping of attributes to their default values or a MOPS expression resolving to such a map. This values are inherited by children objects as defaults when their value is unset. These values are merged with and thus override the ones inherited from the API Object default property.
is_collection
booleanIMMUTABLEDefines whether the resource managed in this path is a collection or not.
false
headers
() => mapIMMUTABLEA mapping of HTTP headers to their corresponding values.
accepts
array[string]IMMUTABLEAn array of content types. The supported content types are:
application/json
application/json; charset=utf-8
application/msgpack
application/msgpack; charset=utf-8
application/x-www-form-urlencoded
provides
array[string]IMMUTABLEAn array of content types. The supported content types are:
application/json
application/json; charset=utf-8
application/msgpack
application/msgpack; charset=utf-8
schemes
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
body_max_bytes
integerIMMUTABLEbody_read_bytes
integerIMMUTABLEbody_read_seconds
integerIMMUTABLEtimeout
integerIMMUTABLEwamp.call_timeout
(See reference).connect_timeout
integerIMMUTABLEretries
integerIMMUTABLEretry_timeout
integerIMMUTABLEdelete
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP DELETE
Request.
get
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP GET
Request.
head
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP HEAD
Request.
options
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP OPTIONS
Request.
patch
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP PATCH
Request.
post
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP POST
Request.
put
OperationObjectIMMUTABLEThe operation specification to perform in case the API Gateway receives an HTTP PUT
Request.
Path Object example
{
"id": "example-api",
...,
"versions" : {
"base_path": "v1.0",
...,
"paths" : {
"/path/to/resource" : {
"get" : {
...
},
"post" : {
...
}
},
"/path/to/:resourceId" : {
"get" : {
...
},
"post" : {
...
}
},
"/path/to/other/resource" : {
"get" : {
...
},
"post" : {
...
}
}
}
}
}
Operation Object
info
stringIMMUTABLEbody_max_bytes
integerIMMUTABLEbody_max_byte
value.body_read_bytes
integerIMMUTABLEbody_read_bytes
value.body_read_seconds
integerIMMUTABLEbody_read_seconds
value.Operation Object example
TBD
Action Object
The API Gateway currently supports 3 types of actions.
Static Action
An action that returns a static response.
type
stringREQUIREDIMMUTABLEThe value static
.
headers
() => mapIMMUTABLEA mapping of HTTP headers to their corresponding values.
body
() => anyIMMUTABLEThe body to be returned with the response.
Action Object example
TBD
Forward Action
An action that forwards the incoming HTTP request to an upstream HTTP endpoint.
type
stringREQUIREDIMMUTABLEThe value forward
.
http_method
stringREQUIREDIMMUTABLEThe HTTP method to be used when forwarding the request to the upstream endpoint. It must be on of the HTTP methods:
delete
get
head
options
patch
post
put
host
() => stringREQUIREDIMMUTABLEThe upstream host.
path
() => stringREQUIREDIMMUTABLEThe upstream path.
query_string
() => stringIMMUTABLEThe upstream query string.
headers
() => mapIMMUTABLEA mapping of HTTP headers to their corresponding values.
body
() => anyIMMUTABLEThe body to be forwarded to the upstream endpoint.
connect_timeout
() => integerIMMUTABLEretries
() => integerIMMUTABLEretry_timeout
() => integerIMMUTABLEForward Action Object example
TBD
WAMP Action
An action that transforms an incoming HTTP request to a WAMP operation.
type
stringREQUIREDIMMUTABLEOne of the following values:
wamp_call
wamp_publish
args
array[() => any]IMMUTABLEThe WAMP message args
.
[]
kwargs
() => mapIMMUTABLEThe WAMP message kwargs
.
{}
timeout
() => integerIMMUTABLEconnect_timeout
() => integerIMMUTABLEretries
() => integerIMMUTABLEretry_timeout
() => integerIMMUTABLEWAMP Action example
{
...
"paths": {
"/accounts" : {
"post": {
"action": {
"type": "wamp_call",
"procedure": "com.example.account",
"options": {"timeout": 15000},
"args" : ["{{request.body}}"],
"kwargs" : {}
},
"response": {,
...
}
}
}
}
}
Response Object
The response object defines what the API Gateway should respond in case of a successful result or error. The purpose of this declaration is to be able to customise the outcome of the action performed according to the Action Object declaration.
The outcome is obtained from the API Context action
property by using an expression such as {{action.result.PROP}}
(in case of a successful result) and {{action.error.PROP}}
(in case of an error) where PROP
will depend on the type of action performed.
on_result
objectREQUIREDA declaration of the desired HTTP response in case the action (as defined in the sibling action
object) was successful.
on_error
objectREQUIREDA declaration of the desired HTTP response in case the action (as defined in the sibling action
object) failed.
WAMP Response Object Example
{
...
"paths": {
"/accounts" : {
"post": {
"action": {
"type": "wamp_call",
...
},
"response": {,
"on_result": {
"body": "{{action.result.args |> head}}"
},
"on_error": {
"status_code": "{{status_codes |> get({{action.error.error_uri}}, 500) |> integer}}",
"body": {
"error_uri": "{{action.error.error_uri}}",
"args": "{{action.error.args}}",
"kwargs": "{{action.error.kwargs}}",
"details": "{{action.error.details}}"
}
}
}
}
}
}
}
Defaults Object
The defaults object is used to define default values for the API specification objects properties.
The API Specification parser will use this object to find a default value for the following keys when evaluating the different objects:
schemes
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
accepts
array[string]IMMUTABLEAn array of content types. The supported content types are:
application/json
application/json; charset=utf-8
application/msgpack
application/msgpack; charset=utf-8
application/x-www-form-urlencoded
["application/json", "application/msgpack"]
provides
array[string]IMMUTABLEAn array of content types. The supported content types are:
application/json
application/json; charset=utf-8
application/msgpack
application/msgpack; charset=utf-8
["application/json", "application/msgpack"]
headers
() => mapIMMUTABLEA mapping of HTTP headers to their corresponding values.
{}
body_max_bytes
integerIMMUTABLEbody_read_bytes
integerIMMUTABLEbody_read_seconds
integerIMMUTABLEtimeout
() => integerIMMUTABLEconnect_timeout
() => integerIMMUTABLEretries
() => integerIMMUTABLEretry_timeout
() => integerIMMUTABLESecurity Object
The Security Object defines the authentication method to be used for an API Version. The supported authentication methods are:
- Basic Authentication
- API Key
- OAuth2
- Client Credentials
- Resource Owner Password
Basic Authentication
type
stringREQUIREDIMMUTABLEA value of basic
.
schemes
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
["http"]
API Key Authentication
type
stringREQUIREDIMMUTABLEA value of api_key
.
schemes
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
["http"]
header_name
stringREQUIREDIMMUTABLE"authorization"
WARNING
CURRENTLY NOT IMPLEMENTED
OAuth2 AUthentication
type
stringREQUIREDIMMUTABLEA value of oauth2
.
schemes
array[string]IMMUTABLEAn array of strings where values can be: http
or https
.
["http"]
flow
stringREQUIREDIMMUTABLEOne of the following values:
implicit
authorization_code
client_credentials
resource_owner_password_credentials
token_path
stringIMMUTABLEThe relative path to use for the obtain/refresh token action.
"/oauth/token"
revoke_token_path
stringIMMUTABLEThe relative path to use for the revoke token action.
"/oauth/revoke"
Default Values
Status Codes
The following are the default values used to initialise the API Context.
{
"bondy.error.already_exists": 400, // BAD REQUEST
"bondy.error.not_found": 404, // NOT FOUND
"bondy.error.bad_gateway": 504, // SERVICE UNAVAILABLE
"bondy.error.http_gateway.invalid_expression": 500, // INTERNAL SERVER ERROR,
"bondy.error.timeout": 504, // GATEWAY TIMEOUT
"wamp.error.authorization_failed": 500, // INTERNAL SERVER ERROR,
"wamp.error.canceled": 400, // BAD REQUEST
"wamp.error.close_realm": 500, // INTERNAL SERVER ERROR,
"wamp.error.disclose_me_not_allowed": 400, // BAD REQUEST
"wamp.error.goodbye_and_out": 500, // INTERNAL SERVER ERROR,
"wamp.error.invalid_argument": 400, // BAD REQUEST
"wamp.error.invalid_uri": 400, // BAD REQUEST
"wamp.error.net_failure": 502, // BAD GATEWAY
"wamp.error.not_authorized": 403, // FORBIDDEN
"wamp.error.no_eligible_callee": 502, // BAD GATEWAY
"wamp.error.no_such_procedure": 501, // NOT IMPLEMENTED
"wamp.error.no_such_realm": 502, // BAD GATEWAY
"wamp.error.no_such_registration": 502, // BAD GATEWAY
"wamp.error.no_such_role": 400, // BAD REQUEST
"wamp.error.no_such_session": 500, // INTERNAL SERVER ERROR,
"wamp.error.no_such_subscription": 502, // BAD GATEWAY
"wamp.error.option_disallowed_disclose_me": 400, // BAD REQUEST
"wamp.error.option_not_allowed": 400, // BAD REQUEST
"wamp.error.procedure_already_exists": 400, // BAD REQUEST
"wamp.error.system_shutdown": 500 // INTERNAL SERVER ERROR
}