API Builder 3.0: API First Simple Parameter Example

In part 1 of this blog post series, we introduced the two main new features of API Builder 3.0, namely API First API development and the new graphical Flow Editor.

In this second post, we’ll dive a little deeper and discuss how to pass in a parameter for API First APIs. First, we will simply echo the parameter back. This will help us learn more about dot.js templates. Then, we’ll use the parameter to query a model and return any matching results.

Simple Echo

Before we dive too deep, let’s create a simple API that passes a parameter and then we’ll echo the parameter back in the reply. This will require us to create a dot.js template to get the parameter and create a reply object.

I designed my API in SwaggerHub as I did in part 1 by creating a new API and using Simple API as the Template and editing it.

The YAML is shown below:

swagger: '2.0'
info:
  description: This is a simple echo API
  version: 1.0.0
  title: Simple Echo API
  # put the contact info for your development or API team
  contact:
    email: lbrenman@axway.com

  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html

paths:
  /echo:
    get:
      summary: echoes parameter
      operationId: echo
      description: |
        Pass in a parameter and have it echoed back
      produces:
      - application/json
      parameters:
      - in: query
        name: inputString
        description: pass a required parameter to be echoed back
        required: true
        type: string
      responses:
        200:
          description: echoed input
          schema:
            type: object
            items:
              $ref: '#/definitions/outputItem'
        400:
          description: bad input parameter
definitions:
  outputItem:
    type: object
    required:
    - status
    - message
    properties:
      status:
        type: string
        example: success
      message:
        type: string
        example: xxx
# Added by API Auto Mocking Plugin
host: virtserver.swaggerhub.com
schemes:
 - https

And the resulting Swagger.json is below:

{
  "swagger" : "2.0",
  "info" : {
    "description" : "This is a simple echo API",
    "version" : "1.0.0",
    "title" : "Simple Echo API",
    "contact" : {
      "email" : "lbrenman@axway.com"
    },
    "license" : {
      "name" : "Apache 2.0",
      "url" : "https://www.apache.org/licenses/LICENSE-2.0.html"
    }
  },
  "host" : "virtserver.swaggerhub.com",
  "schemes" : [ "https" ],
  "paths" : {
    "/echo" : {
      "get" : {
        "summary" : "echoes parameter",
        "description" : "Pass in a parameter and have it echoed back\n",
        "operationId" : "echo",
        "produces" : [ "application/json" ],
        "parameters" : [ {
          "name" : "inputString",
          "in" : "query",
          "description" : "pass a required parameter to be echoed back",
          "required" : true,
          "type" : "string"
        } ],
        "responses" : {
          "200" : {
            "description" : "echoed input",
            "schema" : {
              "type" : "object",
              "properties" : { }
            }
          },
          "400" : {
            "description" : "bad input parameter"
          }
        }
      }
    }
  },
  "definitions" : {
    "outputItem" : {
      "type" : "object",
      "required" : [ "message", "status" ],
      "properties" : {
        "status" : {
          "type" : "string",
          "example" : "success"
        },
        "message" : {
          "type" : "string",
          "example" : "xxx"
        }
      }
    }
  }
}

The API is described below:

GET /echo

It has a required parameter, inputString, which is a string.

The response I am sending is shown below:

{
  "status": "success",
  "message": 
}

An example of calling this API is shown below:

https://arrowclouddev-lbrenman.c9users.io/api/echo?inputString=hello

with response:

{
    "status": "success",
    "message": "hello"
}

The API Builder flow for this API is show below:

The main flow-node in this example is the compose flow-node. That is where we take the input parameter and echo it in the response.

You can see from the screenshot above that the data used to evaluate the template is the entire context, $. Note that I could have set it to $.params to limit the context to just the passed in parameters.

The dot.js template is show below:

{
  "status":"success",
  "message": {{=JSON.stringify(it.params.inputString)}}
}

The important part of the template is:

{{=JSON.stringify(it.params.inputString)}}

Let’s break this down as follows:

  • The {{= }} signifies a runtime interpolation and means we will evaluate the contents at runtime
  • JSON.stringify() converts the contents to a string
  • it is the variable that contains the data passed in. In this example, we passed in the entire context. This means that we can access any variable that we may have stored (as outputs of prior node-flows) and/or the request and response objects
  • it.params.inputString is the parameter that is passed in with the API request

Now, we see how the parameter is placed in the response via the template. Let’s view the output of the compose flow-node below:

The output of the compose flow-node is set to $.value.

We will use $.value as the body of the HTTP flow-node as shown below:

Refer to part 1 for how to set up the HTTP flow-node for the error path (we are basically just setting the status to 400).

We can save and test our API in the console as shown below:

In this example, we showed how to extract a parameter and use it in the reply of the API to create an echo API. In the next example, we will actually use the parameter to query a data set and return the response of the query.

Using the Input Parameter to Query a Model

Now that we know how to pass in a parameter and access it in the flow editor, let’s use this parameter to do something useful like query a data set for a single record.

Again, I created my API definition in SwaggerHub and the YAML is shown below:

swagger: '2.0'
info:
  description: Get an account by account name
  version: 1.0.0
  title: Simple FindByName Account API
  # put the contact info for your development or API team
  contact:
    email: lbrenman@axway.com

  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html

paths:
  /accountbyname  :
    get:
      summary: Parameters
      operationId: findbyname
      description: |
        Pass in the account name to get a specific account
      produces:
      - application/json
      parameters:
      - in: query
        name: accountname
        description: Account Name
        required: true
        type: string
      responses:
        200:
          description: Account
          schema:
            type: object
            items:
              $ref: '#/definitions/account'
        400:
          description: bad input parameter
definitions:
  account:
    type: object
    required:
    - name
    - type
    - phone
    properties:
      name:
        type: string
        example: Genepoint
      type:
        type: string
        example: prospect
      phone:
        type: string
        example: 617-555-1212
# Added by API Auto Mocking Plugin
host: virtserver.swaggerhub.com
schemes:
 - https

The Swagger.json is shown below:

{
  "swagger" : "2.0",
  "info" : {
    "description" : "Get an account by account name",
    "version" : "1.0.0",
    "title" : "Simple FindByName Account API",
    "contact" : {
      "email" : "lbrenman@axway.com"
    },
    "license" : {
      "name" : "Apache 2.0",
      "url" : "https://www.apache.org/licenses/LICENSE-2.0.html"
    }
  },
  "host" : "virtserver.swaggerhub.com",
  "schemes" : [ "https" ],
  "paths" : {
    "/accountbyname" : {
      "get" : {
        "summary" : "Parameters",
        "description" : "Pass in the account name to get a specific account\n",
        "operationId" : "findbyname",
        "produces" : [ "application/json" ],
        "parameters" : [ {
          "name" : "accountname",
          "in" : "query",
          "description" : "Account Name",
          "required" : true,
          "type" : "string"
        } ],
        "responses" : {
          "200" : {
            "description" : "Account",
            "schema" : {
              "type" : "object",
              "properties" : { }
            }
          },
          "400" : {
            "description" : "bad input parameter"
          }
        }
      }
    }
  },
  "definitions" : {
    "account" : {
      "type" : "object",
      "required" : [ "name", "phone", "type" ],
      "properties" : {
        "name" : {
          "type" : "string",
          "example" : "Genepoint"
        },
        "type" : {
          "type" : "string",
          "example" : "prospect"
        },
        "phone" : {
          "type" : "string",
          "example" : "617-555-1212"
        }
      }
    }
  }
}

The API is described below:

GET /accountbyname

It has a required parameter, accountname, which is a string.

The response I am sending is shown below:

{
  "name": ,
  "type": ,
  "phone": ,
}

An example of calling this API is shown below:

https://arrowclouddev-lbrenman.c9users.io/api/accountbyname?accountname=GenePoint

with response:

{
  "name": "GenePoint",
  "type": "Customer - Channel",
  "phone": "(650) 867-3450"
},

Note that for this example, our data will come from a payload optimized Salesforce.com Account model which is exposed through the Axway Salesforce connector. Model based API development has been covered in many blog posts before so we will not cover it in this blog post. The important point to keep in mind is that models are available in the flow editor.

We import the API as before and start a flow and use the Compose flow-node to extract the input parameter as follows:

In the screen shots above, we are doing the following in the Compose flow-node:

  • Setting the dot.js template input to the API input parameters: $.params
  • Stringifying accountname and assigning it to Name, for example, as follows:
  • {"Name": "GenePoint"}
  • Saving the template output to $.where so we can use this in the model query

Note: If this is unfamiliar to you, you may want to review the API Builder model docs to understand how models are queried here

Now, we drag the sfaccount model flow-node onto the canvas. This is a payload optimized model based on the Salesforce Account model and is shown below:

var Arrow = require('arrow');
var Model = Arrow.createModel('sfaccount', {
    "fields": {
        "Name": {
            "name": "Name",
            "type": "string",
            "description": "Account Name",
            "required": true,
            "model": "appc.salesforce/Account"
        },
        "Type": {
            "name": "Type",
            "type": "string",
            "description": "Account Type",
            "model": "appc.salesforce/Account"
        },
        "Phone": {
            "name": "Phone",
            "type": "string",
            "description": "Account Phone",
            "model": "appc.salesforce/Account"
        }
    },
    "connector": "appc.composite",
    "actions": [
        "create",
        "read",
        "update",
        "delete",
        "deleteAll"
    ],
    "description": "payload optimized SFDC account"
});
module.exports = Model;

If you’re unsure how to use the Salesforce connector and create payload optimized models/APIs, refer to the online docs and/or this blog post.

In the screen shots above, we are doing the following in the sfaccount model flow-node:

  • Setting the Method to query so that we can query the model
  • Setting the where parameter to $.where, the output of the Compose flow-node above it (this should be the accountname input parameter composed into a suitable where clause)

Note that the output of the query is an array of models

Before we create the output, we should check to see that the query produced an output. We can accomplish this with the Condition flow-node (Exists) as follows:

We are basically checking to make sure that the query returned at least one account.

Now, we can format our response using a compose flow-node as follows:

In the screenshots above, we are doing the following in the Format Response Compose flow-node:

  • Setting the template input (data) to $.models[0] which we know exists based on the step above
  • Setting the template to correspond to the format of our response as follows:
    {
      "name":"{{=it.Name}}",
      "type":"{{=it.Type}}",
      "phone":"{{=it.Phone}}"
    }
  • Setting Output -> Next to $.response

Now we can format our output using the HTTP flow-node as follows:

In the screenshot above, we are setting the success output by setting the status code to 200 and the response body to $.response.

At this point, we can press save in the upper right hand corner of the screen and test our API. In the screenshot below, I entered “genepoint” as the input parameter:

We can see the response as follows:

{
  "name": "GenePoint",
  "type": "Customer - Channel",
  "phone": "(650) 867-3450"
}

At this point, we simply need to prepare an error response for all of our error paths as follows:

In the HTTP Response Error HTTP flow-node, I am simply setting the status code to 400. This is most likely too simplistic and better error handling should be employed but it is sufficient for our example.

Conclusion

In this blog post, we saw how to pass in API parameters and use API Builder 3.0’s API-First API development flow to both echo back the passed in parameter as well as use the parameter to query a model and return the result. This helps us understand more about how to use the Flow Editor, some new flow-nodes and more around dot.js templating.

Stay tuned for more in Part 3.