When designing Web APIs, many constraints and factors affect the decisions we make as software architects and developers. Some of these constraints are internal (like what tools and libraries are available within our tech stacks), and others external (like choosing a representational format easy for clients to consume, such as JSON). Regardless of the specific constraints facing each API designer, we almost universally want our Web APIs to be easy to use, flexible, and consistent. This article by Jordan Ambra does a great job of explaining these "golden rules" we all hope to follow as API designers.
The best Web APIs allow API users to use a service with minimum effort and knowledge of the system. Presenting the API user with only the information required to perform a task at hand, with links to related information that they may be interested in, allows for the user to focus on using your API to do what they need to do at the moment while having enough context to learn more about your API should the need arise. This scheme leads to APIs that are discoverable: API users can interact with your API from a single entry point in a way that requires little to no prior knowledge, and "discover" the rest of your API as needed.
Having a discoverable API makes it easier for your API users to understand, build mental models of your application and its data. New data and relationships can be found, even without the user knowing of their existence. It also makes it easy to inline documentation with links to external documentation. Client applications built on discoverable APIs have simplified logic since the responsibility of creating link URLs remains with your API, meaning as your API changes and evolves, it's easier to change your URL-structure without breaking client applications.
An architectural style for discoverable APIs
Okay, we can't discuss discoverable APIs without talking about REST. The Representational State Transfer architectural style introduced by Roy Fielding, specifically its Uniform interface constraint, describes a powerful scheme to design discoverable APIs that has influenced virtually every discoverable API. The Uniform interface constraint introduced key concepts of a resource, resource identifier, resource representation and hypermedia controls to describe discoverable distributed systems. I'll be using these concepts, especially through the Richardson Maturity Model to describe an architectural style for asynchronous event-driven APIs. Let's go through these concepts in the context of a synchronous RESTful HTTP system first to familiarize ourselves with the concepts before we go asynchronous.
The Richardson Maturity Model breaks down the core elements of a REST architecture into three steps.
The most basic concept of a REST style API is the concept of a resource. We think about our system as a collection of resources, each resource being a logical entity within the system with a unique resource identifier. Each resource has a resource representation exposing its data in an agreed representation format. A resource can be manipulated using verbs acting on its identifier.
In an HTTP context, our resource identifiers are URLs referencing our resources. We return resource representations as HTTP responses with a chosen content-type like JSON or XML.
Resources within our system can be manipulated using verbs. Verbs define the actions we allow API users to perform on our resources. The REST styles a finite set of verbs to manipulate resources uniformly.
In an HTTP context, we can use the four main HTTP methods,
DELETE as verbs
to fetch, create, update and delete resources respectively.
Hypermedia controls are the final and most consequential concept for RESTful APIs. Hypermedia controls are what make REST APIs discoverable; by including references to a resource and other related resources in its representation and specifying what verbs can act on the resource, we control what actions an API user can take on within our system and can selectively expose information related to the user's context.
In an HTTP context, our resource representation can include the URL identifiers of related resources, the HTTP methods the user can use to send HTTP requests to those URLs, as well as additional information like external links to documentation.
What about asynchronous APIs?
The REST architectural style isn't specific to HTTP or any concrete application-level protocol. At ChatKitty, we applied RESTful principles to develop StompX - a convention for asynchronous event-driven APIs that extends the Simple Text Oriented Messaging Protocol (STOMP) with RESTful concepts and discoverability. REST has been applied extensively to synchronous HTTP APIs with formats and media types like HAL providing concrete conventions and structure for RESTful APIs. As real-time applications become more popular, we believe it's important to have an open standard and conventions to represent resources, their relationships and actions with a REST style to build flexible, open and discoverable APIs.
StompX is a simple set of conventions that gives a consistent and convenient way to hyperlink between resources in asynchronous event-driven APIs and discover actions that can be taken on those resources. StompX provides an opinionated set of conventions, rules, and structures for creating event-driven APIs that are compatible with the Richardson Maturity Model using the STOMP protocol as its underlying messaging protocol.
StompX is based on the concept of resources; the logical entities within your application and their representations. A StompX messaging API returns resource representations inside the text body of a STOMP message frame, inside of an event or relay event. A StompX resource representation is a normal JSON object with your resource state as you would have it any JSON response, as well as metadata providing information about how to fetch more data, take actions, and listen to events related to the resource and other relevant resources.
API users manipulate StompX resources using actions. Unlike in an HTTP RESTful where there are a few verbs, a StompX API consists of multiple actions, each corresponding to a real-world action a user can take to interact with resources within the system. Actions performed changes the state of your application and can trigger API events. API clients perform actions by sending STOMP send frames to an action destination with a JSON text body containing parameters required to perform the action.
Since you can't be event-driven without events, events are first class citizens in StompX APIs. API events
inform your users what happens in your system and allows them to react in real-time.
A StompX event has a type tied to a specific type of action, is always triggered by an action previously
performed and embeds an API resource that was a consequence of causing the action. API users can subscribe
to API topics, listen to events of a specific type and asynchronously perform their own actions as a result.
When an action occurs and triggers an API event, the event is sent via a
MESSAGE frame to API users subscribed
to a topic related to the event.
A StompX resource that can be acted on may expose one or more topics. A StompX topic is a STOMP destination that API users can subscribe to receive events related to the resources. Events sent to a topic may have multiples types but can only embed resources of the same type. API clients can subscribe to a topic while they're interested in events related to the topic and unsubscribe once they are no longer in receiving new events.
Relays let you fetch resources and retrieve data asynchronously using a request-reply pattern. Relay acts like a special action/topic/event, where you subscribe to a relay destination, it triggers a relay event with the data you requested, and automatically unsubscribes you from the relay destination.
Like RESTful APIs, StompX resource representations include hypermedia controls to be discoverable. Resources representations embed the actions, topics, relays relevant to themselves, allowing the API user to traverse through the API.
The StompX specification at this point is still very informal and incomplete. We've been using StompX both internally and externally with our Open Chat API, and we plan on formalizing the specification. As web APIs move towards asynchronous and event-driven architectures, we hope StompX can provide an open and easy way for messaging services to communicate.