API-First Development with Apigee-127 (and Swagger-Node)
This is the fourth and final post in a series examining the considerations that went into building an API to power our intelligent API management platform solution.
In the previous post of this series, we explored frameworks for creating APIs in which the code is generated from the API definition and in which there’s no API definition at all. Here we’ll wrap up by describing a new approach which we proposed based on our experience and on experience with popular frameworks—one in which the API design drives the code.
This new approach is different from the “code-first” approach we described in an earlier post in that the API definition is created before the code may be invoked. It is also different from the code generation approach because there is no intermediate step of generated code to fall out of sync.
This idea resulted in us coming up with the following philosophy:
- The API must be designed at the start
- The contract that represents the API design must drive the API runtime
- The API design will change, so the framework must adapt without the code, design, and documentation falling out of sync
When we say that the design “drives” the code, we mean just that—the document that describes the API design is parsed and turned into a set of data structures that the runtime uses, in real time, to classify, validate, and route each incoming API call.
This bit is the part that most people mis-understand. This approach is not “model-driven,” in which there is some separate artifact that is generated from the code, or that is used to generate code. Rather, the API design is consumed every time the API server is started and used to decide how to process every incoming API call. If there are existing systems that work this way, we are not aware of them.
Keeping the definition and implementation in sync
We believe that there are several advantages to this mechanism, and very few costs. For instance, although the API definition is validated every time the API server is started, in our implementation the API server starts as quickly as any Node.js app—which is to say, very, very quickly.
Most importantly, with this approach it is not possible for the definition of an API and the implementation to fall out of sync. In this case, the API specification is more than a description of a possible truth. It is the definition of truth.
The resulting bit of technology is called “swagger-node.” It consists of a validation component and a runtime component.
The runtime component uses the validated data structure to wire the API design into whatever server the developer is using. Since there are so many servers for Node.js, swagger-node works with the most popular ones, including Express, Hapi, Restify, and Sails, as well as any Connect-based middleware.
For instance, when Express is used, swagger-node works as “middleware” that plugs into the HTTP call processing chain, validates each API call, and routes it to some Node.js code that actually handles the API call.
Although swagger-node is built for Node.js, there is no reason why the “design-driven” philosophy it uses could not be extended to any other environment that supports dynamic invocation, such as Java, Go, or even Erlang. In fact, the swagger-node project has inspired swagger-inflector (in Java) and Connexion (in Python).
Pulling it together: Apigee-127
In addition to the basic API design, the API specification can also include additional API metadata.
For instance, the Swagger 2.0 specification allows the API definition to be annotated with security information, such as what flavour of OAuth to use for particular API calls. It also allows for vendor-specific extensions. These can be used to specify additional information about the API contract, additional documentation fields, or information about policies that apply to the API traffic.
Based on these concepts, we also assembled “Apigee-127,” or “a127.” (The name comes from the IP address of “localhost”—127.0.0.1—and the idea that 127 would allow developers to easily run many of the API management functions of our Apigee Edge product on their laptops).
For instance, Apigee-127 builds upon swagger-node to allow annotations in the Swagger document for:
- OAuth authentication, with the ability to require different “scopes,” or no authentication at all, on a call-by-call basis
- API key validation
- Response caching
- Spike arresting (service-level traffic limits designed to arrest out-of-control API clients)
- Quotas (application- and user-specific traffic limits designed to address business requirements and allow APIs to be monetized)
- API analytics (which are gathered at runtime and pushed asynchronously to the Apigee cloud)
By using Apigee-127, a software developer for the Node.js platform can design an API, handle non-functional requirements without writing additional code, and quickly jump between the API definition and the code using the very short “compile”-edit-debug link that Node.js enables.
Based on our experiences designing and implementing web APIs, we felt that there was room in the world for a different approach for defining APIs and connecting them to code that would result in higher-quality APIs.
The resulting tools—swagger-node and Apigee-127—are available on NPM and GitHub and have been used by many developers to build productive APIs. Please try them out and let us know how they can be better!
Ideas are cheap, but execution isn’t. The people who actually created Apigee-127, swagger-node, and the Swagger editor are Scott Ganyo, Jeff West, Jeremy Whitlock, and Mohsen Azimi, and they were guided by Marsh Gardiner, Ed Anuff, and myself. Special thanks also to Swagger's Tony Tam for his support and encouragement of this project along the way.
Photo: Zhang Wenjie/Flickr