API developers must navigate evolving standards, design intuitive interfaces, ensure security, and address the ever-changing needs of clients. With diverse data formats, authentication methods, and scalability requirements, API design demands a delicate balance between simplicity and robustness, making it a dynamic field that requires constant adaptation and innovation. In this episode, Developer Advocate and seasoned IT industry veteran Nicolas Frankel shares effective strategies for API evolution, delving into the complexities of API design and uncovering the pivotal role of API gateways in modern architectures.
Nicolas Frankel’s over two decades of experience in the IT industry is marked by his diverse roles as a developer, team leader, and architect, among others. Currently, he is a developer advocate at Apache APISIX, engaging in API development discussions and delivering talks. Apache APISIX is an API gateway within the Apache Foundation’s ecosystem, built upon the robust NGINX platform.
API evolution and versioning
Nicolas understands that API evolution is complex enough to merit its own book. At the heart of this complexity lies a fundamental challenge: While adding new fields to an API is generally a smooth process, the real intricacies emerge with data type modifications, field removals, or alterations in field names. These modifications can break the established contract with API clients.
In contrast to user interface (UI) changes, which tend to be more straightforward, backend changes—especially those involving APIs—require careful consideration. As they evolve, APIs must maintain compatibility with existing clients, including applications and systems relying on the API’s functionality. Nicolas reminds us that making such changes can jeopardize the trust-based contract between API providers and their customers, as this contract embodies the expectations and assumptions customers hold about the API’s behavior and the data it supplies.
One approach to navigating these changes while preserving a semblance of stability involves maintaining “the same interface toward the outside world,” Nicolas suggests. In other words, he advises ensuring compatibility with the existing API while handling adaptations internally.
According to Nicolas, API gateways can facilitate this process. API gateways intercept incoming requests, adapt them, and then translate the responses back to the original API version, effectively shielding the client from disruptive changes. In this way, API will remain consistent from the client’s perspective. However, it’s worth noting that this approach may introduce some performance overhead, as the API gateway can add latency to the equation.
Nonetheless, the most effective way to address this challenge is through versioning. Nicolas emphasizes the importance of incorporating versioning into API design and development from the start. He argues that versioning during API evolution enables changes without violating the existing contract with API consumers, thereby mitigating potential issues. Common versioning approaches include:
- Path-Based Versioning:
- The version is included in the URL path, a widely adopted practice due to its clarity and transparency.
- Example: https://api.example.com/v1/resource or https://api.example.com/v2/resource.
- Pros: Provides explicit versioning in the URL, easily understandable.
- Cons: May result in longer and less user-friendly URLs, and necessitates updates to the path in all API requests when changing versions.
- Query Parameter Versioning:
- The version is specified as a query parameter in the API request.
- Example: https://api.example.com/resource?version=v1 or https://api.example.com/resource?version=v2.
- Pros: Allows concise and clean URLs, simple switching between versions in the request.
- Cons: Less conspicuous than path-based versioning, potentially overlooked in the documentation.
- Header-Based Versioning:
- The version is denoted in an HTTP header, such as the Accept header.
- Example: The header might contain Accept: application/vnd.example.v1+json.
- Pros: Keeps URLs uncluttered and accommodates detailed versioning information.
- Cons: Marginally more complex for clients to set up as it necessitates handling headers, yet affords greater flexibility.
The complexities of API design
Within API design, the pursuit of a flawless and rigorously RESTful API is debatable. “There’s no REST by the book,” Nicolas affirms while pointing out that holding a strict adherence to traditional REST principles clashes with creativity and adaptability in API design. Hence, he encourages newcomers to prioritize practicality and effectiveness in their API designs: “An API should prioritize ease of use above all else; If you solely focus on the provider side of the API, you may lack insights,” he says.
While Nicolas recognizes the significance of certain foundational principles that warrant preservation, such as HTTP verbs and methods, he also brings to light stepping beyond the boundaries of CRUD (Create, Read, Update, and Delete) operations. Real-world applications might demand API requirements to extend beyond rudimentary CRUD operations and involve spanning multiple entities, intricate business logic, or operations that defy alignment with standard HTTP methods. Object-oriented programming is a means to match these requirements, yet reconciling with the REST paradigm is challenging. In these instances, developers grapple with complex scenarios that resist categorization within the confines of standard HTTP methods.
In tackling complex scenarios, Nicolas suggests that designers may occasionally resort to crafting specific additional verbs (distinct from standard HTTP methods) or appending supplemental elements to the URL structure.
Simultaneously, Nicolas brings up the importance of consistency in API design: “If you always approach the same problem with the same solution, you add consistency,” he says. Even in instances where solutions veer from the path of strict RESTfulness, it is necessary to maintain a consistent approach to addressing analogous issues across the API. Both developers and API consumers put consistency over rigid adherence to the ideals of pure REST, as it fosters a predictable and user-friendly experience.
In like manner, whether designing an HTTP API or a library within a software stack, Nicolas urges designers to step into the shoes of API consumers and write code that interacts with the API to gain firsthand experience regarding its usability. Nicolas emphasizes that ease of use is of utmost importance because even a stellar product may languish if its accompanying API needs to improve in usability.
Fundamentally, APIs should be designed with maximum usability in mind. Should API designers neglect the client-side perspective and focus solely on the provider side, they may remain oblivious to the challenges faced by developers using their product.
For those open to explore alternative avenues in API design, Nicolas enthusiastically recommends the book API Design Patterns by JJ Geewax. This comprehensive resource delves into unconventional strategies for overcoming the challenges encountered in API design.
API gateways in modern architectures
In modern web architecture, intermediary layers often emerge as a necessity. Nicolas asserts that employing a reverse proxy as a security measure is imperative because “the app shouldn’t be exposed on the internet” without adequate protection. In this regard, a reverse proxy serves as a first line of defense, scrutinizing incoming requests before they reach the API backend.
Still, a reverse proxy may not easily accommodate the dynamic and frequent changes often demanded by evolving business logic. While a reverse proxy can establish rate limits to safeguard against DDoS attacks, applying such rate limits to APIs is difficult, given that not all clients are equal: some may be free users, while others are paid customers with varying limits.
In stark contrast, an API gateway, as described by Nicolas, takes on the role of “a reverse proxy on steroids.” It is a powerful component that handles routing and accommodates the dynamic and frequent shifts in business logic. Given the inherently creative nature of business, frequent alterations in business logic are par for the course. As Nicolas puts it, “business logic changes frequently, and consequently, it will always find new ways to break your stuff.”
API gateways grapple with these intricacies. In the case of the NGINX platform, Nicolas recommends using OpenResty alongside APISIX to reconfigure NGINX through plugins written in Lua language. With these plugins, “you apply the configuration, it’s changed dynamically and everything is fine and dandy and you can isolate the business logic part in this plugin.”
As for the optimal timing for integrating an API gateway into the architecture, Nicolas believes it becomes a practical consideration as soon as a team manages multiple applications or APIs. The API gateway, functioning as a single entry point, will bring numerous advantages, including authentication, rate limiting, and effective management of other infrastructure-related concerns.
Crafting the API tech stack
HTTP and JSON have long stood as the quintessential duo for building APIs, offering a reliable and widely adopted foundation. Nonetheless, the ever-evolving tech landscape continually ushers in new contenders. Among these are GraphQL and gRPC.
Regarding GraphQL, Nicolas draws an intriguing parallel, likening it to a return to SOAP. Both approaches advocate designing APIs with a single point of entry, which, while conceptually appealing, introduces complexities for API gateways. In this context, the API gateway’s responsibilities extend beyond HTTP method routing to payload inspection, a task that can pose security concerns.
On a different note, Nicolas believes that XML, while having fallen out of favor, still boasts certain advantages over JSON. While acknowledging the industry’s shift towards JSON due to its simplicity, he affirms XML makes “hard things simpler,” excelling in data validation and expressing data format requirements.
From a performance perspective, gRPC shines brightly; however, Nicolas reminds us that consumers of gRPC APIs must possess corresponding model definitions, “So if you do breaking changes, then it’s much harder on the clients.” Nicolas proposes a solution in the form of an API gateway like Apache APISIX, which adeptly translates gRPC calls into standard JSON calls.
Furthermore, Nicolas suggests that the choice between JSON and gRPC may hinge on an organization’s development speed and release cycles. As a rule of thumb, gRPC may be better suited for more established, slower-moving companies, while JSON may be the preferred option for agile, fast-paced enterprises.
Best practices for documenting APIs
Nicolas offers a clear perspective on API documentation, asserting OpenAPI’s prominence. He firmly believes that OpenAPI has become the de facto standard for documenting APIs, stating unequivocally that “if you create an API, you should provide an OpenAPI schema.” He also points out the availability of tools that can generate code and documentation based on OpenAPI specifications, catering to a wide range of standard technology stacks.
Moreover, Nicolas underscores the significance of adopting a “contract-first” approach in API design. This approach entails commencing the API design process with the definition of an API contract that encompasses vital details such as endpoints, HTTP verbs, data formats, and more, essentially serving as a blueprint for the API. The flexibility of APIs allows for diverse stacks on both the provider and client sides, and starting with a well-defined contract will facilitate compatibility between these stacks.
The bottom line
Learn more about Nicolas’ work and ideas in his blog, blog.frankel.ch. You can follow him on Twitter and Mastodon.Nicolas’ series of talks on software development are available on his YouTube channel. Moreover, he will offer a talk about API evolution in Belgrade, Serbia, as part of Heapcon 2023.