| title | How to design and build reusable APIs for your business objects according to the Synobsys conventions |
|---|
🏗️ DRAFT This page is under construction
- TOC {:toc}
In this how-to we'll create an ProductOrdering REST API for the Northwind database forge component using a design first approach.
- TODO review this material:
- NLGov API Design Rules
- OpenAPI Specification
- Api stylebook - design guidelines
- REST API Design Guidance
- REST API Tutorial
- API Design Cheat Sheet
- Canonical Models Should Be A Core Component Of Your API Strategy
- [Wikipedia] Canonical schema pattern
- The Web API Checklist -- 43 Things To Think About When Designing, Testing, and Releasing your API
The api must facilitate creating an order.
Derived from the Northwind Order data model we we need the following canonical structures:
| Structure | Description |
|---|---|
| Category | Category canonical structure |
| Customer | Customer canonical structure |
| CustomersPage | List of customers and count |
| Order | Order canonical structure |
| OrderObject | Order and OrderProduct details |
| OrderProduct | OrderProduct canonical structure |
| OrdersPage | List of orders and count |
| Product | Product canonical structure |
| ProductObject | Product detailed structure |
| ProductsPage | List of products and count |
| Shipper | Shipper canonical structure |
To enable product ordering we require the following Api methods:
| Name | HTTP Method | URL Path | Description |
|---|---|---|---|
| CategoryGet | GET | /categories | Returns a list of categories |
| CustomerGet | GET | /customers | Retrieves a paginated list of customers |
| CustomerGetById | GET | /customers/{customer_id} | Returns the customer details for a given customer. |
| CustomerOrderGet | GET | /customers/{customer_id}/orders | Retrieves a list of orders for a specific customer. |
| OrderCreate | POST | /orders | Place an order |
| OrderGet | GET | /orders | Retrieve a paginates list of orders |
| OrderGetById | GET | /orders/{order_id} | Return the details of a specific order |
| ProductGet | GET | /products | Retrieve a paginated list of products that meet the search parameters. |
| ProductGetById | GET | /products/{ProductId} | Retrieve details of a specific product. |
- TODO design canonical schema and api methods see Schema-First API Design
The product ordering api application consists of the following modules:
- NorthwindDB The core service module that exposes the Northwind database
- NorthwindSchema_Lib Library containing the canonical schemas used in the api.
- Northwind_CBL A Canonical Business Logic service module exposing Server Actions in canonical schema format.
- ProductOrdering_API API module exposing the REST API
- Create Application Product API
- Open Lifetime (<your environment >/lifetime/Applications.aspx )
- Click on [Create Application]
- Select the Owner team e.g. Reference Architecture
- Environment: Development
- What are you building: Service
- Name: [Domain concept API] (Sample Product API)
- Description: ?
- Upload icon: upload an meaningful icon
- Create App
- Go to Service Studio, wait until the application synchronized from lifetime and open the application
- Add Canonical Scheme module
- Name: SampleProductSchema_Lib
- Type: Library
- Set the description to: "Sample Product concept canonical schema."
- Publish the module
- Add Canonical Business Logic CBL module
- Name: SampleProduct_CBL
- Type: Service
- Set the description to: "SampleProduct Canonical Business Logic (CBL). Provides server actions to be used for exposing data in web services transforming the Entity records to the canonical schema."
- Publish the module
- Add API Module
- Name: SampleProduct_API
- Type: Service
- Set the description to: "REST API Exposing the sample product concept."
- Publish the module
Because there is no naming convention for rest apis we adhere to the OutSystem PascalCase naming convention
- Open module NorthwindSchema_Lib
- Set the module description to: "Northwind Canonical schema. Provides the canonical structures for use in exposing data with API's"
- Create a structure for the following exposed entities:
- Category
- Customer
- Employee
- Order
- Product
- Shipper
- Supplier
- Steps:
- Open the NorthwindDB module.
- Select an entity to be exposed e.g. Order.
- Copy the Order entity and past it in the library module as a structure
- Remove the Id attribute. As a rule we don't expose internal table record id's
- There will be errors for all references to other entities resolve this by copying the referenced entity and change the attribute to the corresponding structure. E.g. Order.CustomerId - Customer Identifier -> Order.Customer - Customer structure
- Set the Public attribute of the structures to Yes.
- Publish the module
- To accommodate pagination we need to create a list with count structure so that we can return a page of records and a count of the total available records. Steps:
- Create a new "list" structure e.g. ProductsPage
- Add a structure attribute Products type product list
- Add a structure attribute Count, type LongInteger
- Repeat these steps for CustomersPage and OrdersPage
- Publish the module
For each method of the API we must provide a Canonical Business logic server action. This ensures that we don't put business logic in the api implementation and have it available for reuse when exposing other protocols e.g. SOAP.
- Open the Northwind_CBL Module
- Add a dependency to NorthwindSchemaLib Category, Product and ProductsPage
- Add a new server action "ProductGet
- Add the following input parameters:
- CategoryName, type: text, description: Search on category
- Search, type: text, description: Search on Name
- Page, type: Integer, description: Page number for pagination
- PerPage, type: Integer, description: Number of items per page
- Add an output parameter result, type ProductsPage
- Add the following logic:
- Trim the inputs
- Validate the inputs
- Add wildcards to the non empty Search parameter
- Add an aggregate with Product and Category as sources
- Filter the aggregate on Category and Search
- Set Results.Products to the Aggregate Name.List
- Set Results.Count to the Aggregate Name.Count
- Convert the aggregate to SQL
- Add input parameters StartIndex and MaxRecords to the SQL
- Add an Integer structure to the output of the SQL
- Add a separate count aggregate and minimize the joins.
- Add the following line to the end of the SQL:
OFFSET @StartIndex ROWS FETCH NEXT @MaxRecords ROWS ONLY - Set the SQL.StartIndex to (Page-1)*PerPage
- Set MaxRecords to PerPage
- Check that the Results output is correct.
- Install required forge components
- Define the API and methods
-
In service studio open module
ProductOrderingAPI -
Open the logic tab
<Ctrl_3> -
Expand
Integrations -
Right click on REST and select
Expose REST API- Name: V1
- Description: Product Ordering API Version 1
- Authentication: Basic See how to Add Custom Authentication to an Exposed REST API for other authentication methods.
- OnResponse : New OnResponse
-
Add the following logic to the OnResponse action
- REST_CustomErrors_Lib/REST_CustomizeResponse
- Assign CustomizedResponse = REST_CustomizeResponse.CustomizedResponse
- AddHeader Name: Version Value: "1.0.0"
-
Right click on
V1and select Add REST API Method- Name: CategoriesGet
- Input parameters: none
- Output parameters: categories (Category list)
- Description: Returns a list of categories.
- URL Path: /categories
- HTTP Method: GET
-
Repeat this step for each of the following methods:
Name Inputs Output Description URL Path Method CustomerGetById customer_id customer Returns the customer details for a given customer. /customers/{customer_id} GET CustomerCreate customer customer_id Creates a new customer /customers POST CustomerDelete customer_id - Deletes a specific customer /customers/{customer_id} DELETE CustomersGet - customers Retrieves a list of customers /customers GET CustomerUpdate customer - Updates a customer /customers PUT OrderCreate order order_id Place an order /orders POST OrderGetById order_id order Return the details of a specific order /orders/{order_id} GET OrdersGet page, per_page orders Retrieve a paginated list of orders /orders GET CustomerOrdersGet customer_id, status, page, per_page orders Retrieves a list of orders for a specific customer /customers//customers/{customer_id}/orders GET ProductsGet category, search, per_page, page products Retrieve a paginated list of products that meet the search parameters. /products GET ProductGetById product_id product Retrieve details of a specific product /products/{product_id} GET
-
- TODO Appropriate record counting
- Add remaining steps...


