How to wrap legacy systems with REST APIs

When I mention REST to developers, I often find they associate it with the latest APIs from leading modern applications from the likes of Google, Facebook, and Twitter. However, in my experience, pure REST APIs can actually be a major upgrade in how easily and effectively you can interact with legacy services and apps, with similar benefits around simplicity, efficiency, the leanness of APIs, and being detached from the traditional client-server construct.

This article will show you how to harness REST APIs to make your legacy services more usable.

Is it REST, or is it not?

First, let’s get something out of the way. Even though the concept of REST (Representational State Transfer) APIs is well known in the software industry and is well-accepted as an architectural style for web services, the number of misconceptions about its nature is always surprising to me. Perhaps because REST almost always uses HTTP as the protocol for transfer, when people talk about how they use REST, they are often actually referring to a hybrid API of HTTP requests and REST. Not all calls implemented on top of the HTTP protocol can be referred to as RESTful services, even if they are provided using REST frameworks such as Jersey. In fact, some of the HTTP requests often being used have more similarities with the basic concepts of SOAP than with REST.

SOAP is more of a protocol, whereas REST is an architectural style, so a head-to-head comparison isn’t actually constructive, in my opinion. Of course, they both work well in distributed enterprise environments and are language- and platform-independent. However, in SOAP (Simple Object Access Protocol), changes are made by accessing server objects remotely, while REST focuses on representing server objects on the client side (which is the Representational in REST; more on that in the example below). Last, REST, as an architectural style, is simple to understand and closer to the web itself in its design philosophy. 

Statelessness: A few examples

Here are some examples of statelessness that illustrate the value of REST.

Example 1

Say I have in place a requirement management system that acts as a relational database in the back end and answers on the web to the URL http://www.<myservice>.com. In a SOAP scenario, I would need to take the following steps:

  1. Expose a server API for creating requirements (e.g., createRequirement), which receives a set of predefined parameters.

  2. Reveal and control the requirement’s state by exposing a set of getters and setters.

  3. Expose a server API for removing the requirement (e.g., deleteRequirement).

This route can bring about unnecessary complexity. When changing parameters such as name, number, or severity, we are faced with a choice of either 1) breaking the API and making the service temporarily unavailable, or 2) adding another or new API, which, with backward-compatibility considerations, significantly overloads it.

REST, on the other hand, would allow me to simply use two entry points—the set of requirements itself and an ID—as follows:

  • Expose the entire set of requirements - http://www.<myservice>.com/requirements

  • Expose a specific requirement - http://www.<myservice>.com/requirements/{id}

The state would then be controlled by using standard HTTP methods:

  • POST for creating new requirements

  • GET for getting the requirements

  • PUT (or PATCH) for updating them

  • DELETE for deleting them

The stateless nature of REST (as in State Transfer) means if we need to make changes, we simply enhance the representations, while the control methods—the API itself—are left untouched. This makes for a far more efficient and convincing case for scale-out architectures.

Example 2

Here's a situation that could easily confuse many people about the nature of REST: scheduling a meeting. One might think exposing an HTTP-based API from a server for setting a meeting using Jersey or Apache Wink and getting a simple yes or no answer back is REST, but despite the fact it communicates over HTTP and uses frameworks to build RESTful web services, it is not. A meeting—exposed statefully on the server—has multiple attributes. Having to interact with it directly leaves the server in a vulnerable position: Once the server is down for any reason, whether due to a crash or even planned maintenance, it requires the state of the meeting to be either stored partially or discarded. In those cases, if further steps are not performed, we risk losing client information when discarded. A stateful server would need to process data about the meeting in an open session, continuously.

The REST way focuses on the intent, which is the meeting. This means an element (a meeting in this case) would be created on the client, including all relevant parameters, and the client passes on the required information to the server. The meeting can then be exposed and updated accordingly using a CRUD-based mechanism. (CRUD stands for create, read, update, and delete, which are the four basic functions of persistent storage). If a meeting has to be sent, then changing its status—for example, from draft to actual—can then trigger the sending mechanism on the back end, not necessarily in a blocking way. The next step, done from the client side, might be to poll for the meeting’s status, again, using the same CRUD mechanism, achieving indications on whether sending has succeeded and whether attendees accepted.

Example 3

Let’s say we need to access a webpage with cached content, such as a news website with images. The REST resources in this example will be the non-binary ones. Standardizing those resources to be similar as possible to other static web resources will help leverage the benefits of HTTP. Creating a stateless model lets us benefit from the capabilities of caching. Entities that used to be retrieved directly from storage may now be cached on proxy servers or even on the clients, which means reduced latency on the client side, less burden on the server itself, and better scalability and performance. By careful planning we can balance between the benefits of caching and staleness of resources. With SOAP, this is far more challenging.

This also touches on the security aspects of using REST. Since REST is based on HTTP, we can simply secure it via HTTPS. The astute reader would at this stage point out this is true of SOAP as well, as it also uses HTTP. What’s different with REST is, given the leaner and less complicated API, I have significantly fewer endpoints, which means less vulnerability. Protecting a small set of endpoints is easier than protecting an ever-growing API. 

How REST can improve legacy information system efficiency

The above are simple examples of accessing legacy services. Now let me share an example from my own experience.

Our system traces its history back to the mid-1990s, and is actually a set of software products designed for accelerating the delivery of applications. It is made up of a common platform, several key applications, and a dashboard to manage the core lifecycle of applications. The common software foundation contains a consistent repository and an open integration architecture with a supported SDK.

To work with the system’s server, you need a client. In this type of architecture, using SOAP-based web services makes sense, as it is object-oriented and manipulates objects on the server side. REST, and especially pure REST, does not perform manipulations on the server side. Instead, it transfers representations to the client, which is responsible for manipulating them—a much better fit in a relational database environment, because data integrity is ensured.

Remember the requirement management system from Example 1 above? It was based on a real-life use case in our system. As we’ve seen, a requirement becomes an entity and can be simplified to fit into a database, and it is then exposed with a minimal API, to keep things simple and automated for usage and maintenance. Parameters like the date, summary, priority, etc., can be manipulated, while the state is kept on the server—instead of exposing a new API every time I want to change details about a requirement. It’s easier to build, safer to maintain, and more efficient, all thanks to REST.

Designing an effective API

In my experience, the biggest challenge by far is changing people’s preconceptions, which is a barrier to getting everyone aligned and ramping up knowledge. As I’ve shown above, there is a lot of confusion about the very nature of pure REST and how it differs from SOAP or RPC-REST concepts (a.k.a. hybrid APIs; see a primer on RPC), so step one is about education. For example, understanding communicating on HTTP, or using Jersey to expose resources to POST manipulations, isn’t in itself a REST API. The understanding that web and HTTP capabilities should be leveraged as much as possible, and REST fits into them perfectly, is a key factor in providing pure REST APIs.

Then, it’s about changing your approach to building APIs. To make the most of REST on legacy systems, and to avoid overloading the API, a focus on backward compatibility is required. The four methods—GET, PUT (PATCH), DELETE, and POST—are extremely powerful when APIs are built right, especially on top of legacy systems using a relational database for storage. This also requires an investment in the infrastructure and tooling available to the people who build the APIs, and in fostering a DevOps culture.

The next step would be to create an infrastructure for testing. Luckily, REST works well with agile methodologies because it places focus on the development, not the testing. Since we don’t focus on the object anymore, the infrastructure and testing set can be uniform, which makes testing significantly more efficient and effective. In my experience, it takes a matter of minutes, with less room for errors, to move from a ratio of 1:3 testers to developers to 1:15. Also, 100 percent of the code is covered by the APIs, compared with a much lower ratio in legacy apps, where most of the coverage is via manual or automated tests through the user interface. For that matter, the entire set of entities should look the same, behave the same, and support the same functionalities. The following example might help you understand how this approach could work.

Let’s say we have a system that contains several types of entities (A, B, C) that all have different attributes ({a1, a2, a3}, {b1, b2, b3}, and {c1, c2, c3}, respectively). In addition, we want to provide some sorting functionality, which will be written once and then tested for all different sets. Adding another endpoint such as a new type of entity (type D) to the testing mechanism becomes easy. Also note that ideally, all endpoints support the sorting functionality, while differentiation between the endpoints support the sorting functionality and the ones that don’t becomes the challenge.

After covering the functional behavior, the next step is to make sure our REST API is up to the performance challenge. First, define the KPIs for each of the required functionalities. Once defined, testing them continuously should be straightforward, since the API is minimal and behaves similarly for all types of entities.

The implementation phase should be based on the rules laid out above. The application’s behavior should be limited to the set of HTTP methods with their simplest meaning. For example, once the POST method is defined to create objects, engineers should not abuse them for other purposes. Working around the HTTP methods or trying to repurpose them should be avoided. After some time and practice, engineers usually find modeling the system while restricting its behavior to CRUD, though sometimes challenging, is effective and fun.

If you’ve been following so far, you probably understand documentation is quite easy. Since sets of entities behave the same, and instances behave the same, you are left with documenting the general behaviors. The result should be a concise document with no specific explanations regarding specific entities.

Wrapping up REST

There are great benefits in wrapping REST APIs around your legacy app. “I have my way of doing things” may be the right thing for you, but if you want your service to interact externally, you’ll need APIs that are well-designed and easy to test and maintain.

First, understanding the difference between SOAP (or RPC in general) and REST will get you thinking about web architecture and statelessness. Then, using pure REST as a construct will ensure you use the most efficient model, even for legacy information management systems. You'll need to work hard to change organizational preconceptions, improve how you build APIs, and make your testing more efficient so that you will gain significant efficiencies.

Topics: App Dev