Designing a RESTful Web API
An Application Programming Interface (API) simplifies the use of complex actions in software. Technically, an API describes how software components or systems should interact (communicate) with each other (Carver P., 2014). APIs are essential to modern organizations, as they provide a flexible way of working in (.NET Nakama, 2020 August):
- Software Code (e.g., by organizing code, reusing code, providing different restrictions, etc.),
- Software Product (e.g., by providing extensions or new features-product more quickly), and
- Teams or Persons (e.g., by organizing and splitting work, promoting accountability, etc.).
I’m sure you have encountered an application, website, or even a device where the user interface (UI) is designed in a difficult or tricky way. A user interface is like a joke; if you have to explain it, it’s not that good (Martin LeBlanc, 2014 May).
A user interface is like a joke;
if you have to explain it, it’s not that good
In correspondence to what a user interface is for people (e.g., click a button or view data in a website, etc.), an API is for software developers (e.g., use an API function). In both cases, we would like to design something that would (.NET Nakama, 2020 August):
- Solve a problem by understanding the needs of its users.
- Be usable and friendly by understanding the challenges of its users (e.g., limitations, specific needs, etc.) and try to help them make their life a little bit easier 😃
- Have consistent behavior and provide helpful error information. For example, imagine an application where the “Save” buttons have different meanings or the errors look like Figure 2 😛.
In this article, we will see some important points that we can consider when designing an API. Then, we will learn about the Representational State Transfer (REST) architectural style, its constraints, some related terms, and how we can apply it in a Web API.
The design of an API is a necessary process (with many decisions) that organizes and hides software complexity. For that purpose, the principle of information hiding can be applied, which is the process of hiding the complexity of a piece of software (that is most likely to change) into modules of functionality. In addition, it will protect other parts of the software from extensive modifications. In the following sections, we will see some key points that we can take into account when designing an API (Carver P., 2014) (Freeman J., 2019):
In API design, it is crucial to understand the needs of the API users and the covered business cases. Knowing your users and keeping their needs in mind will guide you to:
- Make your API simple, flexible, and quickly adopted for the specific context. A particular design may be simple for one use case but very complex for another (Redhat, 2019). For example, selecting the appropriate authentication or data format (JSON, XML, etc.) depends on the API context.
- Make its users and you happy. For that, you have to understand the challenges of the API users (e.g., limitations, specific needs) and try to help them.
Being aware of the API best practices can help you make the right decision for each case or when you are in a designing dilemma. But, you do not have to follow them blindly! Instead, you will have to examine and determine if a proposed best practice or solution is a good choice for your legacy code, architecture, available time, and resources.
- Do not follow a programming trend just for the sake of fashion.
- Use them where they make sense.
- Always ask yourself, “What this practice or solution will provide?”, “Why do we need it?”.
Examine each decision thoroughly (small or large) by asking yourself the following questions:
- How will this decision affect the API’s usability?
- What additional value will this decision provide to the API users?
- Will this decision result in a breaking-change?
- Document your decisions (i.e., the answers to such questions) for future reference. For example, you will need to re-evaluate a design decision. So, it is essential to document information about your reasoning.
Design an API with consistent behavior (e.g., consistent use of the HTTP verbs in REST) and Error Handling with helpful information. For .NET Core applications, you could use the open-source Consistent API Response Errors (CARE) NuGet library to:
- Centralize the error handling (of validation errors, application exceptions, and unhandled exceptions),
- Return consistent and valuable error information and,
- Simplify the API controllers.
Before selecting or attempting any integration with an API, most developers check out its documentation. Okay, in some cases, we are just trying random stuff for hours 😛 (Figure 3), but still, API documentation is essential.
- The API documentation should contain:
- Guides to getting started (i.e., authentication and first a hello world example) and,
- The complete and updated API reference.
- The time to first hello world (TTFHW) is an important metric that will show how long it takes for a developer to integrate with your API. Examining this metric is an opportunity to put yourself in the shoes of a developer who wants to use your API.
Representational State Transfer (REST) is an architectural style for distributed hypermedia systems that Roy Thomas Fielding created in his Ph.D. dissertation (Fielding R., 2000). REST is based on other network-based architectural styles (Fielding R., 2000) and, combined with additional constraints, defines a uniform connector interface. The following list shows the six constraints (principles) of the REST architectural style.
Applications should use the client-server architectural style to separate the user interface (client) concerns from the business logic and data storage concerns (server). This architectural style applies the separation of concerns (SoC) principle, and thus, each component can evolve independently.
The server should not maintain any state (session). Therefore, the client should manage their application state. In practice, this means that each client request should contain all the necessary information to be handled from the server.
A stateless server provides some significant advantages, such as visibility (each request is its resource), reliability (easier recovery from failures), and scalability (easier deployment in multiple servers). However, the main disadvantage is that it may decrease the network performance by increased transferred data.
The server should implicitly or explicitly label the response data as cacheable or non-cacheable. In this way, the client can reuse the same data in later equivalent requests (usually for a limited timeframe), with partial or no interaction with the server.
Caching can improve the efficiency and scalability of the system and will enhance the user experience (UX) in general. However, we have to pay attention when characterizing a response data as cacheable or non-cacheable because this may affect the system’s reliability. For example, let’s assume that our client project needs some data (e.g., the user’s balance) to perform several other actions. If the balance is outdated, the user will not have the expected UX.
The REST architectural style emphasizes a uniform interface between components, in which the information is transferred in a standardized form. This uniform interface is achieved by using the following architectural constraints to guide the behavior of the components.
- Identification of resources: The resources should be uniquely identifiable through a single URL (Uniform Resource Locator).
- Manipulation of resources through representations: Perform actions on a resource by using a representation of the intended state (e.g., the data in JSON, XML, bytes, etc.), including all the necessary metadata to describe the intended state. For example, in a REST WEB API, an action can be represented as an HTTP Verb (GET, POST, etc.) and the metadata as HTTP headers.
- Self-descriptive messages: The transmitted data should contain all the necessary information to perform the desired action.
- Hypermedia as the Engine of Application State (HATEOAS): Hypermedia is the simultaneous presentation of information and controls in REST. The information becomes the affordance through which the user (or automaton) obtains choices and selects actions (Fielding R., 2008). In simple words, the server should provide information for all available actions of each resource. For example, when retrieving data of a bank account (e.g., balance information), the server could also return the URLs of possible actions, such as to deposit, withdraw, etc.
The layered system style is used in REST, which allows our architecture to be composed in hierarchical layers. However, each layer should be independent and not able to “see” beyond its layer. In this way, we can improve the system’s scalability, performance, and security by adding load-balancers and proxies (Goebelbecker E., 2018).
An optional constraint within REST is that it allows clients to download and execute code (in WebAssembly or scripts) to extend their functionalities. This constraint can improve the system’s extensibility by downloading features after deployment, but it reduces visibility.
REST-Based terms are usually used in several articles to characterize a service, but what’s the difference?
REST is an architectural style that defines six constraints (principles). We could use the REST term to describe a service that follows all the constraints mentioned above. However, there are cases in which some of the constraints cannot be applied easily in a specific context. For that reason, developers use the following two terms to distinguish these cases:
- RESTful: Refers to services that follow all REST constraints.
- REST-Based: Refer to services that follow some of the REST constraints. Usually, in these cases, the HATEOAS constrain is not applied.
A REST Web API (or REST API) is based on the HTTP protocol to perform actions in our resources using the five basic HTTP verbs (GET, POST, PUT, PATCH, and DELETE).
A resource is any information that can be named, an entity (e.g., a person, a ticket), document, file, etc. A group of resources is called a collection.
The location of a resource is identified by using URLs. For example, if we build a REST Web API for products, we could use the following URLs. In general, the base URL is the consistent part of this location.
|/products/||A collection of product resources.|
|/products/ab1245/||A resource with information about a specific product (e.g., with the identifier ab1245).|
On each resource, we can perform all CRUD operations using the basic HTTP verbs. In the following table, we can see some action examples per HTTP verb. To create a CRUD Web API with ASP.NET Core, you can follow the .NET Nakama (2020, October 4) tutorial.
|HTTP Verb||URL||CRUD Operation||Description|
|POST||/products/||Create||Create a new product resource from the data transmitted in the HTTP Message Body. The data should contain all the necessary information to create the resource.|
|GET||/products/||Read||Retrieve a list of the products. Then, based on our implementation and resources, we can consider returning the resources in pages (pagination).|
|GET||/products/ab1245/||Read||Retrieve information about the product with id: ab1245.|
|PUT||/products/ab1245/||Update||Update the existing resource with id: ab1245, from the data transmitted in the HTTP Message Body. It is crucial to note that the whole resource will be updated. Thus, all the information that represents the resource should be sent.|
|PATCH||/products/ab1245/||Update||Partially update the existing resource with id: ab1245, from the data transmitted in the HTTP Message Body. This means that we can update only a few properties of the resource. For example, we could only change the status of the product.|
|DELETE||/products/ab1245/||Delete||Delete the resource with id: ab1245.|
Designing an API is a continuous process of improvements, based mainly on the needs of the API users and on the new features-products. The goal is to organize and hide the complexity of software code (and the business logic) to protect other parts of the software from extensive modifications. For that purpose, we should design our Web APIs carefully by examining all decisions thoroughly. In this article, we showed some key points that we can consider when designing an API.
Representational State Transfer (REST) is an architectural style that guides us to create Web APIs, based on the HTTP protocol, to perform actions in our resources using the five basic HTTP verbs (GET, POST, PUT, PATCH, and DELETE).
The REST architectural style defines six constraints (principles) that should be applied: 1) client-server architecture, 2) stateless server, 3) label data as catchable, 4) uniform interface, 5) layered system, and 6) optionally serve executable code. Based on the number of followed constraints in service, it can be referred to as
RESTful (all constraints are followed) or as
REST-Based (some constraints are followed).
REST is a great architectural style because it provides a simple way to create APIs based on the existing HTTP methods, which can be used in almost every device. But this API approach will not work for every organization. Instead, we should design and build our APIs based on consumer needs.
In the following article, we will examine some more practical cases when designing a REST Web API. Stay tuned!
- .NET Nakama (2020, August 4). So… What is an API?. https://www.dotnetnakama.com/blog/what-is-an-api/
- Carver P. (2014, June 21). Your API Is Bad (And You Should Feel Bad). https://leanpub.com/yourapiisbad/read
- Fielding R. (2000). Architectural Styles and the Design of Network-based Software Architectures. https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
- Fielding R. (2008, October 20). A comment for the Hypertext definition. https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-718
- Freeman J. (2019, August 8). What is an API? Application programming interfaces explained. https://www.infoworld.com/article/3269878/what-is-an-api-application-programming-interfaces-explained.html
- Goebelbecker E. (2018, December 11). REST vs. RESTful: The Difference and Why the Difference Doesn’t Matter. https://blog.ndepend.com/rest-vs-restful/
- Martin LeBlanc (2014, May 14). A user interface is like a joke. If you have to explain it, it’s not that good. https://twitter.com/martinleblanc/status/466638260195041280?s=20
- Redhat (2019, January 8). What is API design?. https://www.redhat.com/en/topics/api/what-is-api-design