Background

Recently, due to business requirements, I participated in the development of the cloud product CSB need to open the Open API to the public, originally it was not a difficult task, because our internal Open API open mechanism has been very mature, there is no need for me to design, but this time the demand is mainly for some independent deployment scenarios, the need to design a set of specifications, that means, the need for Open API for That means that there is a need for some specification constraints on the Open API, so I have this article.

Open API and front-end pages are always the face of the product, so if the Open API is not standardized, it will lower the professionalism of the product. In the cloud scenario, many users will choose to build their own portals to connect to the Open APIs of cloud products, which puts forward the demand for us to build a mature Open API mechanism.

From the business perspective, there are some guiding principles to guide us to improve the Open API mechanism.

  • The interfaces used in front-end pages and the interfaces provided by Open API are the same set of interfaces
  • Any front-end page interface should have a corresponding Open API

From a technical point of view, there are many API open standards for our reference, and the Open API documentation of some open source products is also very complete. On the one hand, I will take the essence of them, on the other hand, we should consider the specificity of our own product output form. In this article, we will try to explore a suitable Open API open specification around many factors.

Open API Design Considerations

What exactly should a well-designed Open API specification specify?

From the design point of view, we need to consider: naming specification, composition specification, path specification, in/out specification, data type specification, uniform return value specification, error code specification, paging specification.

From the team’s point of view, whether the back-end junior and mid-level developers and front-end developers in the team have enough experience to understand and implement the API specification. At the same time, along with the staff turnover, whether the Open API specification can be passed on well.

From the industry’s point of view, we need to consider whether the market where the Open API is provided is mature and the API style may already have a corresponding specification.

From a product perspective, the API style is different for each product, and we will focus on this perspective below.

In short, the design of Open API is something that is difficult to conclude. Before I introduce the Open API specification that my product eventually adopts, I will first talk about some familiar concepts, such as restful.

The restful specification debate

If you’ve been around the code world, you’ve heard of the restful specification.

  • add, delete, change should be declared as: POST, DELETE, PUT, PATCH, GET
  • verbs should not appear, verbs are unified by the HTTP Method
  • Reflect the abstraction of “resource”
  • Use pathVariable, queryParam, header, statusCode to express a lot of business semantics

The restful specification may look good, but if you’ve actually tried to get it off the ground, you’re bound to encounter some similar problems.

  • Take the user login interface as an example, such interface is difficult to map to resource addition, deletion, and checking.
  • Take querying the error rate of interface requests in the last 7 hours as an example, derived to complex query scenarios such as graphQL, which often requires a json structure, GET is unable to achieve this, only POST can be passed.

For this reason, the restful specification is gradually gaining opposition.

  • Forcing everything to be “resourceful” goes against common development sense, and interfaces can’t always be mapped by simple additions, deletions, and queries.
  • Complex query semantics can’t always be expressed in GET.

The restful style has its fans, and there is no shortage of objections to these statements, and the community is full of comments like “the main people who reject the restful style are low-level, uninspired architects and front- and back-end programmers, and it’s the people who can’t design, not the specification”. At the same time, restful was sublimated: the problem of retrieving complex parameters should have been classified as post in the restful semantics, because the behavior is not the location of resources (GET), but the retrieval of resources (POST).

This obviously irritates the nerves of restful style opponents, who disdainfully say, “Oh, silly restful fundamentalists.

I wonder if you are a restful fan or opponent? Or maybe you are a neutral.

This is the end of the restful debate, which is purely fictional, so don’t worry about it. Regardless of how you view restful, you can be a neutral in my discussion below, otherwise the effect is halved.

ROA and RPC

API design is not just a restful specification, in a larger perspective, the mainstream API design style can actually be divided into

  • Resource oriented design, i.e. ROA (Resource oriented architecture)
  • Process-oriented design, i.e. RPC (Remote Procedure Call)

restful is a typical example of ROA style, while RPC style is relatively less familiar, but in fact, most of the system interfaces are probably RPC style, but the concept of RPC style is not well known.

Take the CRUD of the user module as an example, and compare the two styles.

ROA style

Create User (POST)

1
2
3
4
5
6
7
8
9
Request:
POST /users

{"name": "kirito", "age": 18}

Response:
HTTP 201 Created

{"id": 1, "name": "kirito", "age": 18}

Query user (GET)

1
2
3
4
5
6
7
Request:
GET /users/1

Response:
HTTP 200 OK

{"id": 1, "name": "kirito", "age": 18}

Query user list (GET)

1
2
3
4
5
6
7
Request:
GET /users

Response:
HTTP 200 OK

{[{"id": 1, "name": "kirito", "age": 18}], "next": "/users?offset=1"}

Create/modify users (PUT)

1
2
3
4
5
6
7
8
9
Request:
PUT /users/1

{"name": "kirito", "age": 19}

Response:
HTTP 200 OK

{"id": 1, "name": "kirito", "age": 19}

Modify user (PATCH)

1
2
3
4
5
6
7
8
9
Request:
PATCH /users/1

{"age": 20}

Response:
HTTP 200 OK

{"id": 1, "name": "kirito", "age": 20}

Delete user (DELETE)

1
2
3
4
5
Request:
DELETE /users/1

Response:
HTTP 204 No Content

ROA style and restful specification is the same thing, in order to facilitate its comparison with RPC style interface, here are some points of interest in the above example.

  • Using HTTP response codes (200, 201, 204) to complete the mapping of HTTP semantics and business semantics, exception streams also appear 404, 401 and so on (for space considerations, this article does not do the introduction of exception streams)
  • PATCH part of the modified resource, the request body is the content of the modified part; PUT create/modify the resource, the request body is all the content of the new resource
  • id is the resource locator, while age and name are attributes

RPC style

Create User (POST)

1
2
3
4
5
6
7
8
9
Request:
POST /user/createUser

{"name": "kirito", "age": 18}

Response:
HTTP 200 OK

{"code": 0, "message": "", "data": {"id": 1, "name": "kirito", "age": 18}}

Query user (POST)

1
2
3
4
5
6
7
8
9
Request:
POST /user/getUser

{"id": 1}

Response:
HTTP 200 OK

{"code": 0, "message": "", "data": {"id": 1, "name": "kirito", "age": 18}}

Query user list (POST)

1
2
3
4
5
6
7
Request:
POST /user/listUsers

Response:
HTTP 200 OK

{"code": 0, "message": "", "data": {"user": [{"id": 1, "name": "kirito", "age": 18}], "next": "/user/listUsers?offset=1"}}

Modify user (POST)

1
2
3
4
5
6
7
8
9
Request:
POST /user/modifyUser

{"id": 1, "name": "kirito", "age": 19}

Response:
HTTP 200 OK

{"code": 0, "message": "", "data": {"id": 1, "name": "kirito", "age": 19}}

Modify user name (POST)

1
2
3
4
5
6
7
8
9
Request:
POST /user/modifyUserAge

{"id": 1, "age": 20}

Response:
HTTP 200 OK

{"code": 0, "message": "", "data": {"id": 1, "name": "kirito", "age": 20}}

Delete user (DELETE)

1
2
3
4
5
6
7
Request:
POST /user/deleteUser

{"id": 1}

Response:
{"code": 0, "message": ""}

The RPC style is not like the restful ROA style, there are some agreed specifications, each business system in the implementation, there are differences, so this is only the author’s personal experience, but I hope the reader can seek common ground while preserving differences.

  • user is the name of the module, do not need to use the plural form like ROA style.
  • Use an explicit verb-object structure instead of mapping CRUD to HTTP Method, which uses POST uniformly, and GET for query scenarios.
  • The return value carries code, message and data to map the response status and response information, generally you can define your own code status code, this article uses 0 to mark the success of the request, message is only meaningful when the business response fails, data represents the business response results.

The decision of how to choose RPC and ROA is based on the business situation of the product itself. There are the following guiding principles.

  • RPC style is appropriate for APIs with complex business logic that cannot be described by simple add, delete, change and check.
  • If the industry standard of the business requires restful style API or ROA to meet the business requirements, it is appropriate to use ROA style.

AWS mainly uses RPC style, Azure, Google mainly uses ROA (restful) style, AliCloud OpenAPI supports both RPC and ROA, with RPC as the main.

Although the specification is innocent, but in the ROA style in the process of practice, I have seen a lot of “traps”:

  • Require resources first, i.e., design resources first, then design interfaces, which requires high software development process.
  • Wrong ROA design case 1: tomcat and other application servers do not allow the request body to be carried by default when handling HTTP requests for DELETE methods, and need to be explicitly enabled, resulting in deletion failure. (This case is a designer’s problem, complex deletion scenarios should not be mapped to DELELE, but should be changed to POST, DELETE should not carry the request body).
  • Wrong ROA design case 2: the parameters carried in the restful path may cause regular matching problems, such as mistakenly using email as a path parameter, or conflicting multi-level path matching problems (this case is a designer’s problem, complex query scenarios should not be mapped to GET, but should be changed to POST, only resource locators should appear in the path, and should not carry attribute).
  • When the response code is 404, it is difficult to distinguish whether the path really does not exist, or the resource does not exist
  • Not good for docking gateways and other scenarios that require configured route forwarding.

CSB’s Open API specification hopes to meet the following requirements.

  • A clear design concept for back-end development to design interfaces, so that they don’t have to spend too much time on resource abstraction (which doesn’t mean that resources don’t need to be designed) because they don’t have to struggle with whether to use POST or GET for an interface.
  • When front-end development interface, it is faster to collaborate with the back-end and facilitate the encapsulation of the front-end interface.
  • The overall style of the user interface to the Open API is consistent and the modules are clear In summary, I plan to adopt the RPC design specification in the design style selection. To summarize the advantages of RPC style.
  • API design is less difficult, easy to implement
  • Most of the mature IAAS layer products in Aliyun use RPC specification.
  • Suitable for complex business scenarios

A detailed example of RPC interface documentation

Creating a service

request parameters

Serial Number Field Name in Chinese Field Name in English Data Type Required Description
1 Name name string yes Displayed name
2 protocol protocol string yes Enumerated values:http/grpc/webservice
3 loadbalanced lb string yes enumeration value: random/roundrobin
4 upstreamType upstreamType string yes enumerated values: fixed/discovery
5 node list nodes array no upstreamType=fixed required, example: [{host: 1.1.1.1,port: 80,weight: 1}]
6 origin id originId string no
7 serviceName serviceName string No Name in the registry, required when upstreamType=discovery
8 service description description string no
9 gateway id gatewayId string yes

return parameter

Serial Number Field Name in Chinese Field Name in English Data Type Description
1 response code code int 0 identifies success; 1 identifies failure
2 Response Message message string
3 response result data string return service id

Example of a request

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /service/createService

Request:
{
  "name": "httpbin",
  "protocol": "http",
  "lb": "random",
  "upstreamType": "fixed",
  "nodes": [
    {
      "host": "httpbin.org",
      "port": "80",
      "weight": "1"
    }
  ],
  "gatewayId": "gw-1qw2e3e4"
}

Response:
{
  "code": 0,
  "message": "",
  "serviceId": "s-1qw2e3e4"
}

request sample API naming convention

  • APIs should be spelled correctly in English and conform to grammatical conventions, including singular and plural, tense and language conventions
  • No multiple APIs with similar meanings but no real difference in functionality, such as /user/getUser and /user/describeUser at the same time
  • Language conventions: Pinyin is prohibited
  • The naming rules are fixed for the following common scenarios
    • Parameters of date and time type should be named XxxxTime. e.g. CreateTime
  • Common operation name specification
    • creat: create
    • modif: change
    • delete: delete
    • get: get individual resource details
    • list: get a list of resources
    • establishRelation: create a resource relationship
    • destroyRelation: destroy a resource relationship

Summary

Take for example one of the specifications promoted in this article: “Use POST for all interfaces”. This is not to accommodate low-level, unmotivated architects and front- and back-end programmers (as I’ve seen in the community forums), but to improve development efficiency, reduce communication costs, reduce the cost of operations and error location, and invest the cost of blind tossing in other areas such as business architecture design, testing systems, online monitoring, disaster recovery and downgrading.

The interface specification is not only RPC and ROA as I summarized, there are also some comments to classify GraphQL as a separate class of API design style for complex query scenarios, interested students can refer to es API documentation.

To sum up, I plan to adopt the RPC API design style.