Neben anderen Schlagwörtern, wie z. B. Software-as-a-Service (SaaS) gibt es heutzutage auch den...
Switchboard API Design Guideline
The present document is primarily defined for API designers. It is intended to give them a concise and clear guide on how to design good APIs. Good APIs are always designed from the viewpoint of the consumer (user of the API). For him an API should be easy to understand and easy to use. When designing an API, it is therefore not the primary concern that the provider of an API should have as little effort as possible. Furthermore, we want to achieve that APIs have a minimum quality and a certain uniformity. The guideline leaves enough leeway to design the APIs technically according to the requirements of the individual products or to further increase the "maturity level" of APIs. For a more in-depth introductinon to the topic, references are given in the appendix.
APIs express most purely what our systems do and are therefore extremely valuable business resources. The design of high- quality, long-living APIs have become even more important as external systems are now to be connected to the application architecture.
Against this background, "API First" is regarded as one of the most important architectural principles. The development of REST services starts with the API definition outside the code and ideally includes extensive peer review feedback to achieve high quality APIs.
Our claim therefore:
- are easy to understand and learn
- are general and abstract from specific implementation and use cases are robust and easy to operate
- have a common appearance
- follow the REST architecture principles
- comply with HTTP standards
The structure follows RFC 2119’s specification:
MUST - This requirement must be implemented.
SHOULD - The requirement should be fulfilled. In jusGfied cases an exception can be made.
MAY - It is recommended to implement this requirement.
The more detailed specification of MUST/SHOULD/MAY can be found in RFC 2119.
3. Principles & Standards
REST stands for "Representational State Transfer". For the first Gme these conventions were described in the doctoral thesis of Roy Fieldings. Besides some guidelines that REST services follow, communication between client and server is based on the HTTP protocol (see De 2017, p. 29-32).
For REST services, APIs always point to resources, such as a customer's data. Resources are localized using so-called URIs, which show the path to such a resource. The common CRUD operations are implemented using HTTP methods (GET, POST, PUT, DELETE, ...). When a HTTP request is sent to a Web service, the Web service sends a response with a HTTP status code that tell the requesting client whether or not its request was successful. Both the HTTP methods and the status codes were defined in RFC 7231 (see Fielding and Reschke 2014).
The design of REST APIs is primarily governed by the "6 constraints for building a RESTful architecture", which Roy Fielding first described in his dissertation.
These six restrictions are: Uniform Interface, Client Server, Stateless, Cache, Layered system, Code on demand (optional).
According to Fielding, the following REST principles are derived from these restrictions:
Identification of Resources
Resources are idenGfied via an URI. Each URI is assigned to a single resource and all access to that resource is done through that URI.
Manipulation of Resources through Representation
When a resource is requested, the server responds with a representation of the resource. This representation captures the current status of the resource in a format that the client can understand and edit.
The representations provided by a system contain all the data that the client needs to understand and respond to the resource.
Hypermedia as the engine of application state
HATEOAS at REST maturity level 3. The first three restrictions taken together ultimately imply "hypermedia as the engine of application state". (Cf. Fielding 2000, pp. 76-86).
Why are we doing this?
- Scalability: Web services under this architectural style do not need to store the status of a communication between server and client. They save time and storage space and thus capacities for expanding performance.
- Reliability: Statelessness increases reliability by facilitating recovery processes after errors. A faulty service can be quickly restarted because its state is basically irrelevant.
- Modularity: It is also advantageous that external systems of machines can be integrated to any time via an interface.
- Independence: Because the components of the architectural style run independently, stable communication is possible. The advantage is comparable to a website from which individual pages can be exchanged without having to adapt the other pages.
3.2. OpenAPI specification
The technical specification of an API constitutes the API contract in the true sense of the term. It provides developers who want to link applications to the interface with the relevant information. Examples would be the "endpoints" of an API, that is, the resources that can be addressed with the API, as well as the possible HTTP methods that can be applied to them. The specification format for the API should be the OpenAPI Specification 3.0 of the Swagger Framework (see De 2017, p. 78-79).
In the example below you can see very well the structure of the API. It is described using the YAML syntax. Compared to JSON, YAML offers better readability (fewer brackets, no quotation marks, etc.).
Since the OpenAPI Specification 3.0 it is possible to store the individual components in separate files. This ensures the clear reusability of resources and improves the clarity of the individual files. This results in a similar structure as can be found in code (example in chapter 15. Best & Bad Practices).
Why are we doing this?
- Simple and understandable, easy to learn and understand
- Strong tool ecosystem (editor, code generator, test tools)
- Possibility to specify the API completely technically
- Approaches to technical description through “descriptions”
- There is a possibility to explore the API live from the documentation and send sample requests (Swagger UI).
3.3. API First
If APIs are to be perceived and highlighted as a product, the API First approach makes this possible. The API stands in the foreground by first defining the contract between the systems. Only then is it possible to develop interfaces in the best possible way and to guarantee that systems can communicate across their interfaces without transformations. The requirements result from the interface. This means that the API is leading in the design of the services. The database design (for relational databases) then also results from the interface. Existing processes can also be optimized more easily in this way, as there are no dependencies on functions that have already been implemented. Furthermore, the API First approach allows development teams to work on a product in parallel, since all relevant information is already specified.
The principle of AaaP (API-as-a-Product) must be predominant in the design phase, but also in all other phases. Interfaces are the figurehead of sohware, they are what the consumer can see of the software. Good, robust and, above all, user-friendly APIs therefore have top priority. For this reason, both the AaaP idea and the principle ofcustomer centricity are an integral part of an API strategy.
3.5. Customer Centricity
Both the API and the entire application are always described and defined from the customer/user perspective.
For this reason, the strategy for an API always depends on the strategy of a company, because it is in a way the figurehead for sohware within the application landscape, i.e. what a customer or developer can see of the sohware first. But this can also be adapted for internal APIs. The user does not necessarily have to be an external consumer but can have the same needs internally (see Medjaoui et al. 2018, pp. 60-61).
4. General Guidelines
6. Versioning & Compatibility
6.1. Major/minor versions (see also semver.org)
Minor version (for "Non-Breaking Changes"):
- If optional parameters are added.
- If additional endpoints with advanced features are added.
- If the value range of parameters is increased.
- If descriptions and metadata are changed.
- If responses are added.
Major version (for "Breaking Changes"):
If endpoints are completely changed.
If schemas have new mandatory properGes
If schemes have fewer properGes than before.
If the value range of at least one parameter is reduced.
If the type of a property is changed.
If the header data is extended.
There is no difference in the endpoint URLs between different minor versions to
ensure backward compatibility.
With a new minor version, the client does not have to change the version in the endpoint. With a new major version, the version number is changed.
Breaking Changes, i.e. new major versions, should be avoided as far as possible.
- 7. Deprecation/Pensioning of APIs
- 8. HTTP Requests
An HTTP method is called idempotent if the multiple execution of a request has the same side effects as a single execution. Whether a request for deleting a resource is executed once or several times, the result remains the same; the resource has been deleted. Idempotency is an interesting call semantics for distributed systems, since it enables a request to be repeated if an error occurs.
GET is always idempotent in any case. The GET method never changes the state of a resource. Accordingly, sending a GET request repeatedly only results in the repeated query of the resource.
PUT is also idempotent. It is used to update an existing resource. If the request is sent more than once, the resource is overwritten several times with the same change.
DELETE is also an idempotent method per se. For example, if you delete a Device with a certain ID, this will return the status code 404 (Not Found) on the second attempt, because the resource has already been deleted. Care should be taken when designing methods like DELETE /item/last. This would always delete the last resource, in which case DELETE would not be idempotent.
POST is generally not idempotent. If there are multiple requests, a new resource with a new ID will always be created. This may be desirable if this is provided for in the business process.
However, it can be a problem if the request is automatically repeated in asynchronous scenarios for technical reasons to correct the error (technical retry). For example, if a POST / item is made, the API may not respond with a unique HTTP status code in a reasonable time. The caller ends the request after a certain time (timeout) and automatically repeats the call again in a short time. Depending on whether or not an item was already created with the first request, a duplicate could now be created.
The API implementation should be made robust against such erroneous changes. If the client sets the X-Request-ID uniquely, as required, multiple calls can be detected.
More information can be found in RFC 7232.
- 10. HTTP Status Codes & Errors
HTTP status codes can be used to specify the responses to incoming requests more precisely. The table below lists the most common codes. Others are specified in RFC 7231.
The resources are identified and referenced via URIs (Uniform Resource Identifier). These contain the name of the resource and the network address via which it can be reached.
The following request addresses a list of items.
E. g. http://www.foo.com/v1/item
For example, a parGcular resource could be addressed via the ID.
E. g. http://www.foo.com/v1/item/123
Sub-resources can be accessed via a logical hierarchy.
E. g. http://www.foo.com/v1/item/123/subitem
To filter the resources according to certain restrictions, so-called query parameters are used (for GET, PATCH and DELETE).
E. g. http://www.foo.com/v1/item?item-type=example
Resources must always be defined as nouns in REST. Whether these are specified in the plural or
singular is ohen disputed in the IT world.
The plural notation would automatically imply that there are several items. Which is often desired.
For example, if you request only one resource via the ID, this is much easier to read in the singular. Since code classes are ohen generated from the API, the singular is also more suitable in this case.
Think about which style suits you best. The only thing that is important is that you keep it consistent throughout the API!
12. OpenAPI Specification & Data Formats
Messages in a REST systems must always be self-describing. Headers specify certain restrictions on requests and responses. They can be regarded as metadata. Idempotent calls can be implemented using the X-Request-ID (for more information see also 9. Idempotence).
14. Documentation, Implementation & Operation
15. Best & Bad Practices
From my personal experience and many other insights of API designers, Best & Bad Practices could be developed, which should give good tips and support to avoid common mistakes.
These tips are to be understood as subjective experiences, which however are often based on standards and protocols (e.g. the HTTP protocol). We will be happy to answer any further questions on the points below.
Consistent application of API First
This ensures that dependencies are quickly identified, all necessary information is already specified. In addition, the code for the implementation can be generated from it. This also ensures that API and implementation do not differ from each other.
What is expected must be specified in advance!
Headers define restrictions on requests (for example, Content-Type specifies which MIME type is specified (application/json)).
Using the Swagger Editor
- Generation of code snippets from the OpenAPI specification
- Generation of HTML files from the OpenAPI specification
Using camelCase and Singular for Properties
API First also means that the API serves as the basis for code generation, so using Singular and camelCase for properties makes it easier to convert to code later.
Correct use of HTTP status codes
Correct requests also deliver positive responses (2xx status codes), even if no content exists for the resource. Example 1: If, for example, a list of Devices is requested, but it is still empty, an empty list is returned, no error.
Example 2: If, for example, a specific Device is requested which does not exist, the response with a 404 status code is correct.
Correct specification of the data types
Always use data types as they are defined in the JSON schema.
For example, boolean values can only ever be true or false. 1 and 0 are no valid values.
Outsourcing of components in the OpenAPI
This allows the individual components to be reused across multiple APIs. The files are also smaller and clearer. An example of the structure of an API is shown in the adjacent figure: Different workspaces can be created for different modules. This makes it possible to use the components also across different workspaces.
Standards are not followed
The HTTP protocol, DIN standards and all RFCs mentioned in the guidelines are well-defined guidelines based on many years of experience, which should be followed. Especially when programming, the following applies: You do not have to reinvent the wheel, what already exists should be used!
Differences between documentation and actually implemented instance
The client does not know which is the leading medium, especially since it cannot actually see the implementation. It is important that documentation and implementation match. GeneraGng the code from the API can prevent this problem.
Method names or verbs in the paths
This is clearly defined by the REST conventions. Verbs are already specified by the HTTP methods. Further verbs would therefore only impair readability and thus make the client's understanding worse.
Z. B. GET /findCustomerbyId/1234 -> unnecessary double entry
Error messages in body, without corresponding error code (HTTP status code)
Errors also always require a corresponding HTTP status code. In general, these are 4xx or 5xx codes.
No or insufficient documentaton
The primary goal of an API should be that the client can easily understand it and use it independently (Customer Centricity). No or poor documentation will make it very difficult for the client to achieve this goal. The basic rule is: "As much documentation as necessary, as little documentation as possible."
Transfer payload in the URL
Query parameters are used to specify simple data queries; they are not suitable for passing payload information. The content-type applica1on/json suggests that the payload is formaCed in JSON, making it easily writable with OpenAPI and transferred in the HTTP body.
Additional: API Practices if you hate your customers
De, Brajesh (2017): API Management. An Architect's Guide to Developing and Managing APIs for Your Organization. New York City: Apress Media LLC.
Fielding, Roy Thomas (2000): Architectural Styles and the Design of Network-based Sohware Architectures. Dissertation. University of California, Irvine. Online verfügbar unter hCps://www.ics.uci.edu/~fielding/pubs/ dissertation/top.htm, last checked on 03.06.2019.
Fielding, Roy Thomas; Reschke, Julian (2014): RFC 7231 - Hypertext Transfer Protocol
(HTTP/1.1): SemanGcs and Content. Ed. by the Internet Engineering Task Force (IETF). Available online at hCps://tools.ieÇ.org/html/rfc7231, last checked on 11.04.2019.
Medjaoui, Mehdi; Wilde, Erik; Mitra, Ronnie; Amundsen, Mike; Lane, Kin (2018): ConGnuous API Management. Making the right decisions in an evolving landscape. Sebastopol:
O'Reilly Media Inc.
Some principles and approaches are based on the API Guidelines of Zalando:
Zalando SE (2020): Zalando RESTful API and Event Scheme Guidelines. Ed. by Zalando SE. Available online at hCps://opensource.zalando.com/resÇul-api-guidelines/zalando-guidelines.pdf, last reviewed on 26.02.2020