How to Build an API Interrogation Tool Part 2 of 3
Or Postman 2: The MailPersoning
In Part 1, we took a look at Postman, a nifty tool for developers to help interrogate and debug their API endpoints. I had decided to clone the core functionality of the app: the ability to specify the HTTP method, the target URL, the headers and body of a query; and also the ability to maintain a history of previous queries and to click on one of those entries in order to set up a new query with the same parameters.
This post will delve into the architecture of Mailperson, our Postman clone. We’ll take a look at its major components and the structure of its web client. You’ll also see some details of how to implement the HTTP query constructor using AngularJS.
Breaking it Down
So what would the architecture of such an app look like? Most of the functionality is client-side, namely the interface for the user to specify what the request looks like. You’d also need a server in order to serve up the client files, but the fact that we want to maintain state between sessions, namely the history of queries that have been made previously, means that the app will need some server-side functionality.
In theory, the server could just write the list of queries to a file and read from it whenever it wanted to retrieve the current state of the list, but let’s go ahead and use a database instead. This choice would also decouple the server from managing the list state and allow the application to scale by replicating servers and offloading the complexity of multiple concurrent reads and writes to the database management software. Besides, concurrency and maintaining data integrity is a hard problem a little outside of the scope of this exercise.
Getting to Know The Client
I chose to use AngularJS for this exercise, which is an MVC framework for implementing web applications. Here’s a diagram of the client’s architecture:
You can see that the app is divided into the following components: App, which is the top-level component in which all state is stored; the ControlPanel, which provides the user with the forms in which to enter the values for the URL, headers, and body; the StatusPanel, which displays the status of the most recent query; and the HistoryPanel, which will display the list of previously-issued queries. Note that while the model is depicted in a box of its own, that is merely to indicate what state is being tracked as opposed to being a separate component.
The Data Must Flow (It’s bound to, anyway)
Now let’s take a look at data flow within the Client. All the client’s data, or state, is being stored in the App component. This state, otherwise known as the Model in MVC, consists of the essential query information, the status information from the last issued query, as well as the history list of queries which have been made. So we can store each query in an object and store the history in a list of said objects.
We then bind the values of the current query object in the top level app to the values of the input forms in the Control Panel. AngularJS allows you to bind the values in the model to the views which display them. This means that the Views will dynamically re-render to reflect changes in the the top-level model. In fact, AngularJS grants you the ability to bind the data two-ways, so that any changes in the bound data effected by a controller will be reflected in the model without needing to pass the values up or down the component hierarchy via a function call. I didn’t use two-way binding this time around, but you can see how binding is done in app.html below where the query object and the submitQuery function being passed as parameters to the Control Panel component.
The second part of the data binding equation is a declaration in the Control Panel component to explicitly bind values in that component’s controller. This happens on lines 3–6 below.
Each of the Control Panel, Status Panel, and History Panel components are Views, the V in MVC. A view takes state and presents it from the app to the user. For example, the status object which in the Model is passed down to the Status Panel module in order to display the results of the most recent query.
Finally, the C in MVC stands for controllers. In this case, there are controllers within the Control Panel and History Panel components to translate user input into method calls to operate on the data in the Model.
For example, a reference to the top-level function issueQuery is passed down to the Control Panel component so that it may be invoked when the click event is detected on the SEND button. This is what’s happening on line 5 in the code snippet below, where submitQuery is being invoked on the event of a click on the button element.
When submitQuery is called, it takes the values of the Method, the Method Headers and the Method Body and it issues an XMLHTTP request to the Target Endpoint via a call to the issueQuery method, defined in a service component attached to App.
AngularJS provides a library function $http which allows you to make arbitrary XMLHTTP requests, given the right options. This is what’s at the heart of MailPerson — all the options which the user specified are captured by the Control Panel controller and set in the top-level model. The results of the the $http call are then stored in the Status Code, Response Headers and Response Body via a callback.
As the status object had been previously bound down to the view in the Status Panel object, the view is updated as soon as the values within the model change. This then avoids the need of making further function calls to re-render the Status Panel view when the status object changes.
Next time, on MailPerson
Whew! Now we know how to structure our clone and most of its client. Next time, we’ll take a look at the History Panel and what it means for the server and database part of the equation.