In this part of the REST API Library series we will create the order API based on DAWs order entry sample database.

We will take a look at additional features such as paging, selections as well as additional features to schemas/models.

to get started lets create the order api controller by adding a new http handler based on the cszWebAPIController class.

//
Object oOrderAPI is a cszWebAPIController
    Set psControllerDescription to "allows viewing, adding and deleting orders"
    Set psControllerTitle to "Order API"
    Set psControllerVersion to "1"
    Set psPath to "api/v1/order"
    Set psVerbs to "*"

    Object oVendor_DD is a cVendorDataDictionary
    End_Object

    Object oInventory_DD is a cInventoryDataDictionary
        Set DDO_Server to oVendor_DD
    End_Object

    Object oSalesPerson_DD is a cSalesPersonDataDictionary
    End_Object

    Object oCustomer_DD is a cCustomerDataDictionary
    End_Object

    Object oOrderHeader_DD is a cOrderHeaderDataDictionary
        Set DDO_Server to oSalesPerson_DD
        Set DDO_Server to oCustomer_DD
    End_Object

    Object oOrderDetail_DD is a cOrderDetailDataDictionary
        Set DDO_Server to oInventory_DD
        Set Constrain_file to OrderHeader.File_number
        Set DDO_Server to oOrderHeader_DD
    End_Object
    
    Set Main_DD to oOrderHeader_DD
    ...
End_Object

 

Next we will define our schema/model for the API.

Procedure OnDefineModelColumnsCustomer
    Send AddTableColumn File_Field Customer.Customer_Number
    Send AddTableColumn File_Field Customer.Name
    Send AddTableColumn File_Field Customer.Address
End_Procedure

Procedure OnDefineModelColumnsInventory
    Send AddTableColumn File_Field Inventory.Item_ID
    Send AddTableColumn File_Field Inventory.Description
    Send AddTableColumn File_Field Inventory.On_Hand
    Send AddTableColumn File_Field Inventory.Unit_Price
End_Procedure

Procedure OnDefineModelDetail
    Send AddTableColumn File_Field OrderDetail.Detail_Number
    Send AddTableColumn File_Field OrderDetail.Order_Number
    Send AddTableColumn File_Field OrderDetail.Item_ID
    Send AddTableColumn File_Field OrderDetail.Qty_Ordered
    Send AddTableColumn File_Field OrderDetail.Price
    Send AddTableColumn File_Field OrderDetail.Extended_Price

    Send AddModelParent "inventory" (RefProc(OnDefineModelColumnsInventory)) True
End_Procedure

Procedure OnDefineModels
    Send BeginModel "order"

    Send AddTableColumn File_Field OrderHeader.Order_Number
    Send AddTableColumn File_Field OrderHeader.Customer_Number

    Send AddModelParent "Customer" (RefProc(OnDefineModelColumnsCustomer)) True

    Send AddModelChild "Detail" OrderDetail.File_Number (RefProc(OnDefineModelDetail)) True

    Send AddTableColumn File_Field OrderHeader.Order_Date
    Send AddTableColumn File_Field OrderHeader.Terms
    Send AddTableColumn File_Field OrderHeader.Ship_Via
    Send AddTableColumn File_Field OrderHeader.Ordered_By
    Send AddTableColumn File_Field OrderHeader.SalesPerson_ID
    Send AddTableColumn File_Field OrderHeader.Order_Total
    Send AddTableColumn File_Field OrderHeader.Last_Detail_Num

    Send EndModel

    Send BeginModel "orderlist"

    Send AddTableColumn File_Field OrderHeader.Order_Number
    Send AddTableColumn File_Field OrderHeader.Customer_Number
    Send AddTableColumn File_Field OrderHeader.Order_Date
    Send EndModel
End_Procedure

 We are definfing a schema called order. This is our main schema and contains the  order header fields as well as a parent schema for customer. We also added a child schema for the detail lines as well.

In addition we are declaring a second schema called ‘orderlist’ which we will use to demonstrate using multiple schemas in an API controller.

Now lets declare our standard GET route to return a list of orders

 

//
Send RegisterPath "GET" "" (RefProc(WebAPI_Get)) 
Set psPathSummary to "Get List of orders"
Set psPathDescription to "Use this API to retrieve a list of orders"
Send AddPathresponse 200 "successful return of list of orders" "application/json" True "order"

just like the inventory example we simply declare a GET route with the default WebAPI_Get method, summary and description info for the API documentation.

We also define the response

A test of the new API returns all orders in a json array. This can lead to a lot of data of course.

In order to limit the amount of data returned we want to add paging functionality. The REST library supports required and optional paging and the paging settings can be made for each controller as well as for each path.

to enable paging we can add the following code to our controller

// enable required paging
Send SetPaging WEBAPI_PAGING_REQUIRED 50 20

This line will set the controllers paging default to required with a maximum of 50 records per page and a default of 20.

this will make 3 changes to the API.

first the output will automatically be governed by the paging settings.

second on the input side we can now supply the following query parameters

  • page
    the current page to be returned
  • pageSize
    number of rows to return. this cannot be greater than the maximum page size defined on the controller or route

ad last but not least the return data will include a X-Pagination http header containing the information on the page returned.

The X-Pagination header contains a json object with the following fields

  • PageSize
  • CurrentPage
  • TotalCount (not with embedded db)
  • TotalPages (not with embedded db)

the X-Pagination header can be used by the calling program.

In addition to paging we want to be able to sort the orders by different sort orders. We need to declare these so we can allow the client to set the sort order

lets add the following to our route delcaration

Send AddPathOrder "orderNumber" 1 True 

Send AddPathOrder "orderDate" 3 True
Send AddPathOrder "Customer" 2 False
Set psPathDefaultOrder to "orderNumber"

this will declare 3 different sort orders, by order number, order date as well as customer. The first two allow descending orders as well.

And last we set the default sort order to order number.

this now enables the client to send two additional query parameters

  • order
  • desc

for example

/api/v1/order?order=orderDate&desc=true

or with paging

/api/v1/order?order=orderDate&desc=true&page=10

Of course we also need to be able to select orders for example by date. Lets add some functionality to do just that.

First we modify the orderheader datadictionary to add the constraint logic

Object oOrderHeader_DD is a cOrderHeaderDataDictionary
    Set DDO_Server to oSalesPerson_DD
    Set DDO_Server to oCustomer_DD

    Property Date pdDateFrom ""
    Property Date pdDateTo ""

    Procedure OnConstrain 
        If (pdDateFrom(Self)<>"") Constrain OrderHeader.Order_Date ge (pdDateFrom(Self))
        If (pdDateTo(Self)<>"") Constrain OrderHeader.Order_Date le (pdDateTo(Self))
    End_Procedure

    Procedure ClearConstraints
       Set pdDateFrom to ""
       Set pdDateTo to ""
       Send Rebuild_Constraints
    End_Procedure
End_Object

now we add a new method for our order API

Procedure WebAPI_GetOrders
    Date dFrom dTo
    Get UrlParameter "dateFrom" to dFrom
    Get UrlParameter "dateTo" to dTo

    Set pdDateFrom of oOrderHeader_DD to dFrom
    Set pdDateTo of oOrderHeader_DD to dTo
    Send Rebuild_Constraints to oOrderHeader_DD

    // call original get API
    Send webAPI_Get

    Send ClearConstraints to oOrderHeader_DD
End_Procedure

this new method gets the url parameters ‘dateFrom’ and ‘dateTo’ and uses these values to initialize the DDs constraints

then we simply call the original GET API to get the data and then clear the constraints

we need to modify the route declaration to call this new method

Send RegisterPath "GET" "" (RefProc(WebAPI_GetOrders))

this will now allow the user of the API to specify the dateFrom and dateTo parameters to constrain the orders returned.

Of course all these changes are also made to the OpenAPI documentation as well

Now as a last feature lets see how we can use a second schema in the same API to return the same dataset with a different schema

All we need to do is to declare a new route as follows

Send RegisterPath "GET" "/orderlist" (RefProc(webAPI_Get)) "orderlist"

as you can see in this route we are calling the same WebAPI_GET method but we are  specifying a different schema called “orderlist”.

this will return the same dataset but with a much smaller number of fields.

In the following posts in this series we will look into detailed features such as authentication and more.

 

 

Michael Salzlechner is the CEO of StarZen Technologies, Inc.

He was part of the Windows Team at Data Access Worldwide that created the DataFlex for Windows Product before joining StarZen Technologies. StarZen Technologies provides consulting services as well as custom Application development and third party products