Skip to content

Operations

Within the operations sections you should create an operation for each endpoint of your API. The name of the operation must match the name of the openapi specification, additionally containing the HTTP-method directly in the name. Leading to the following structure:

'HTTP-Method /ENDPOINT_PATH'

HTTP-METHOD Endpoint Name Pattern
GET /news 'GET /news'
POST /news/{id} 'POST /news/{id}'
DELETE /news/comment/{id} 'DELETE /news/comment/{id}'

An operations consists of two sections:

  1. Fulfillments
  2. Responses
  3. Triggers
operations:

  'get /news':
    fulfillments:
      ....

    responses:
      ....

Fulfillments

A fulfillment is the mechanism used to retrieve and process data for each operation in APICHAP. Everytime an endpoint from APICHAP is called, it will run all fulfillments specified for this operation after each other. A fulfillment will mostly gain a set of data which can then be used in all upcoming fulfillments and the responses.

A fulfillment reading data from a database could look like that:

- name: ff2
  type: read
  datasource: db
  instructions:
    - if: PARAM(language) == TEXT(en)
      query: Select * from news WHERE lang = :[PARAM(language)]
    - query: Select * from news
  exceptions:
    - if: SIZE(VALUE(ff2, $.id)) == INTEGER(0)
      statuscode: 404
      message: "No news entries available"

A fulfillment contains the following attributes:

Field Description Mandatory
name Set a fulfillment name. You may use the name later to use your fulfillment results in conditions or response building. x
type Define the type of your fulfillment. READ, UPDATE or LOOP x
datasource The datasource this fulfillment should use to retrieve data from. Use the datasource name from the datasources section. x
if Specifies an if condition. The fulfillment should only be processed under certain conditions.
instructions Define your instructions. An instruction is the actual command on how to retrieve data from your datasource. Like a SQL query, Http-Call, ... x
exceptions Use exceptions to immediately return a HTTP response to the user.
loop Define a JSONArray to loop through and process each entry with a list of sub fulfillments.
fulfillments In combination with loop you are able to define a list of fulfillments to run in a loop
loopbreak A list of conditions. If they match, the current loop is aborted and apichap will continue with the next fulfillment.
index If you run a fulfillment multiple times in a loop, you are able to index each iteration by a value.

IF

Sometimes you want to process a fulfillment only on a certain condition. For example, if the input Header-Parameter 'Content-Language' is available.

# Only run fulfillment if Content-Language header is not empty or null
if: PARAM(Content-Language) != NULL()

# Only run a fulfillment if another fulfillment called 'ff1' (already executed) has at least one entry
if: SIZE(VALUE(ff1, $)) > INTEGER(0)

Instructions

Datasource

The structure of an instruction depends on your datasource Type:

  1. Database
  2. REST Request with JSON response

Read more information on our supported datasource´s here: Datasources

Instructions IF

No matter what kind of datasource you are using you are always able to set a list of instructions with an if condition.

Field Description Mandatory
if Define an if condition under which you want to use this query
query / request Depending on your datasource you either have a query, request, ... x

Database

A instruction for a database type is fairly easy as you simply provide the SQL query. Flexible parameters are set using the APICHAP placeholder syntax.

The APICHAP placeholder syntax is secured against SQL Injections

instructions:
  # The first query will run if language is set. Get news by input parameter Content-Language  
  - if: PARAM(Content-Language) != NULL()
    query: SELECT id FROM news WHERE lang = :[PARAM(Content-Language)]
  # This query is only called if no query before was already matched and executed.
  - query: SELECT id FROM news

REST Request with JSON response

A REST request is specified by setting a ´request´ attribute in your yaml. Within this ´request´ attribute you are able to set the following attributes:

Field Description Mandatory
url The URL for the request to send. x
httpType Your httpType from our supported list: GET, POST, PUT, DELETE. x
header Set http header parameter in the header section.
body Optional you may specify a requestbody for POST, PUT, ... requests.
multipart Send a multipart request

Specify headers by using a ´Key: Value´ approach.

instructions:
  - request:
      url: "https://www.example.com/news"
      httpType: GET
      header:
        - "Content-Language: :[TEXT(en)]"
        - "X-UserId: :[VALUE(ff1, $.userid)]"

Requestbody

Building a requestbody for your request is possible by using the following fields:

Field Description Mandatory
contentType Specify the format of your schema. Currently only "json" is supported x
schema Find out how to construct your requestbody schema here: JSON Schema x

A instruction calling a POST request could look like this

instructions:
  - request:
      url: "https://www.example.com/user"
      httpType: POST
      header:
        - "Authorization: :[PARAM(Authorization)]"
      body:
        contentType: json
        schema:
          email: PARAM(email)
          name: PARAM(name)
          userType: TEXT(user)
          phone:
            list: LIST(1)
            item:
              id: PARAM(phone)

Multipart

If you are in need of using a multipart for sending your request you can simply do so with the attribute multipart. This example shows you how you are using an incoming multipart file within the key image and send it to another request.

instructions:
  - request:
      url: https://www.example.com
      httpType: POST
      header:
        - "Content-Type: image/jpeg"
      multipart:
        file: MULTIPART_FILE(image)
        imagesize: PARAM(imagesize)

Exceptions

In order to handle error cases you are able to specify exception for each fulfillment. Whenever an exception is triggered the processing of the fulfillments is aborted and the exception´s response is returned directly. It´s possible to define multiple exceptions.

Field Description Mandatory
if Define an if condition under which you want to trigger this exception x
statuscode The http status code you want to return for the error case. e.g.: 404 x
message The response message you want to return x

A classical case is to return HTTP-code 404 when an item is not available in the database. Your exception for this case could look like this:

# we have a fulfillment getting a news entry from a database and want to return 404 if the news id does not exist.
name: ffnewsdetail
type: read
datasource: db
instructions:
  - query: Select * from news WHERE id = :[PARAM(id)]
exceptions:
  - if: SIZE(VALUE(ffnewsdetail, $)) <= INTEGER(0)
    statuscode: 404
    message: "Item is not available"

Loop

Sometimes you need to run a list of fulfillments in a loop. For example if you have a requestbody and need to process each entry. This is possible by defining a loop with a list of sub fulfillments. A loop fulfillment holds the following attributes:

Field Description Mandatory
name Set a fulfillment name. You may use the name later to use your fulfillment results in conditions or response building. x
type LOOP x
loop Define a JSONArray you want to loop through. x
fulfillments In combination with loop you are able to define a list of fulfillments to run in a loop x

Within a loop you are able to get the current loop json by using the loop´s fulfillment name.

A loop that wants to insert every entry of the POST requestbody array would look like this:

'post /news/bulk':
  fulfillments:
    - name: loop_news_body
      type: LOOP
      loop: VALUE(BODY(), $.news)
      fulfillments:

        # Insert the news entry
        - name: news-insert
          type: UPDATE
          datasource: mydatabase
          instructions:
            - query: INSERT INTO news (id, title) VALUES (:[VALUE(loop_news_body, $.id)], :[VALUE(loop_news_body, $.properties.title)])

Fulfillment Results within a loop are not available outside of the loop.

Using loop on an object to read a map

If you have a json dictonary and need to loop through each key, you are able to use loop. The loop method will then transform your json to parse into the following structure using key for the json attribute name and val for the value.

{
  "water": {
    "id": 12,
    "bottle_size": "0.7"
  },
  "cola": {
    "id": 13,
    "bottle_size": "0.3"
  },
  "orange_lemonade": {
    "id": 14,
    "bottle_size": "0.5"
  }
}

Each entry is now transformed into a key and val value.

Field Description
key The json attribute name
val The value of the attribute

You can now use this key & val for your loop:

'post /drinks':
  fulfillments:
    - name: loop_drinks
      type: LOOP
      loop: VALUE(BODY(), $)
      fulfillments:

        # Insert the news entry
        - name: news-insert
          type: UPDATE
          datasource: mydatabase
          instructions:
            - query: INSERT INTO drinks (id, name, size) VALUES (:[VALUE(loop_drinks, $.val.id)], :[VALUE(loop_drinks, $.key)], :[VALUE(loop_drinks, $.val.bottle_size)])

Loopbreak

A list of conditions. If they match, the current loop is aborted and apichap will continue with the next fulfillment.

  # Call a paged request by using loop(10) but abort the loop as soon as the request page is empty.
  - name: loop_ff
    type: loop
    loop: LIST(20)
    fulfillments:

      # Retrieve inventory per page
      - name: get_inventory
        type: READ
        datasource: api
        instructions:
          - request:
              url: "https://www.example.com?page=:[VALUE(inventory_page, $)]"
              httpType: GET
        loopbreak:
          - SIZE(VALUE(get_inventory, $.inventory)) <= INTEGER(0)

Index

If you run a fulfillment multiple times in a loop, you are able to index each iteration by a value. It makes sense to index your fulfillment in a loop by an id or unique value. Afterwards you are able to retrieve the results using the VALUE_BY_INDEX method.

  - name: loop_ff
    type: loop
    loop: VALUE(products, $)
    fulfillments:

      # Retrieve inventory per page
      - name: get_inventory
        type: READ
        datasource: api
        instructions:
          - request:
              url: "https://www.example.com?page=:[VALUE(inventory_page, $)]"
              httpType: GET
        index: VALUE(@, $.id)

Responses

After all fulfillments have been executed you are able to build the response for your endpoint. You may specify a list of responses as you are able to use different kind of responses depending on the status. A response has the following attributes:

Field Description Mandatory
name A name for identification x
default True or False. Each operation must have a default response. The default response is used if no error occoured. x
statuscode The http statuscode you want to return. x
schema Find out how to construct your json schema here: JSON Schema

Find out how to construct your json schema here: JSON Schema

# A simple response sending http statuscode 200 and building a simple Json
responses:
  - name: success
    statuscode: 200
    default: true
    schema:
      id: VALUE(contactFF, $.results[0].id)
      firstname: VALUE(contactFF, $.results[0].properties.firstname)
      lastname: VALUE(contactFF, $.results[0].properties.lastname)
      email: VALUE(contactFF, $.results[0].properties.email)
      last_sync: VALUE(contactFF, $[0].update_time)

Full Example

A full operation to get a news item from a database could look like this:

'get /news/{id}':
  fulfillments:
    - name: news
      type: READ
      datasource: db
      instructions:
        - query: Select id, title from news WHERE id = :[PARAM(id)]

    - name: news_images
      type: READ
      datasource: db
      instructions:
        - query: Select imageurl from news_images WHERE imageId = :[PARAM(id)]

  responses:
    - name: success
      statuscode: 200
      default: true
      schema:
        id: VALUE(news, $[0].id)
        title: VALUE(news, $[0].title)
        images:
          list: VALUE(news_image, $)
          item:
            imageurl: VALUE(@, $.imageurl)

Triggers

It´s possible to run an operation also by using timebased triggers. Triggers can be used additionally to an working endpoint operation, as well as by a standalone (not used as endpoint) operation. In the second case, the operation will not need a response configuration.

A trigger consists of:

Field Description Mandatory
name A name for identification x
cron A timebased cron condition: 0 0 * * * * x
# Run this operation every minute
'only_timebased_operation':

  triggers:
    - name: minutely_trigger
      cron: 0 * * * * *

  fulfillments:

    - name: do something in this fulfillment
      type: READ
      ....
# This operation will be triggered by the request GET /test as well as timebase every hour.
'GET /test':

  triggers:
    - name: every_hour
      cron: 0 0 * * * *

  fulfillments:

    - name: do something in this fulfillment
      type: READ
      ....

  responses:
  # As this operation has an HTTP trigger (GET /test) it needs a response.