I’ve recently been working with APIs more than ever. A colleague recently introduced me to Bruno, an offline, open-source API client. For the most part, I had been interacting with APIs using swagger UIs in product or with PowerShell’s Invoke-RestMethod
. This is sometimes challenging, such as remembering complex URLs, managing headers, or handling authentication. Bruno provides a standalone GUI to help streamline some of these tasks. As I was getting up to speed with the interface, I reviewed several prior posts to connect to various APIs I was familiar with. The following notes are what I learned while getting started with Bruno.
Example 1: APC BackUPS
In a previous article we explored creating our own custom ‘API’ using a powershell HTTP listener. This was a very basic example as it required no authentication or special headers. Since this API is so simple, its an easy first example for Bruno.
- Create new collection
- Create new request
- Name the request (
getStats
) - Specify the URL (
http://servername.example.com:6545/getStats
) - Run (the right arrow at the end of the URL).
- Name the request (
The response should show our JSON body that we crafted in our script. Now if I need to run this again, I don’t have to remember the hostname/port number for my service, I can just hit go and get the current response.
Example 1b: Using a Bruno ‘environment’
In the top right of a Bruno collection, there is a drop down list that says ‘No Environment’. If we select that drop down and ‘Configure’ we can create a new environment. This environment is a place to store variables, like server names, ports, and credentials. For my example, I’m going to create an environment named ‘server room’. In the ‘server room’ environment, I’ll define a variable named apcBackupsHost
with the value servername.example.com
). With this environment variable defined, I can edit my URL to use this variable name, enclosed with a pair of squiggly braces as shown below:
If I had multiple hosts running this API, I could create different environments for each. That way I can toggle between them using the environment drop down list and not need to update any of my API calls. Using this environment functionality can help save time when working with different environments (e.g., production vs. staging) and they can help prevent errors when managing credentials or server names.
Example 2: VI/JSON
The next example comes from a prior post as well — Using VI/JSON with Powershell. VI/JSON was introduced in vSphere 8.0U1 as a way of accessing the vSphere Web Services SDK via a REST interface. To get started with this in Bruno, we’ll make a new collection with a POST request to login. We’ll also make an environment for this collection that has four variables:
- VC = vCenter Server name or IP
- vcVersion = the version used in our request (8.0.3.0 in my case)
- username = the username used to connect to vCenter Server
- password = the password used to connect to vCenter Server.
I’ve named my request ‘Login’ and set a few properties. First the URL is https://{{VC}}/sdk/vim25/{{vcVersion}}/SessionManager/SessionManager/Login
, which contains two of the variables from my environment. The body
of the login contains the other two variables, as pictured below:
In addition to the Body I’ve made two other tweaks to this request. You can see where tweaks have been made in the above screenshot… any tab with a change has an indicator count. I’ve outlined the specific changes below:
- Headers:
- Name =
Content-Type
- Value =
application/json
- Vars > Post Response:
- Name:
vmware-api-session-id
- Expr:
res.headers['vmware-api-session-id']
- Expr:
- Value =
- Name =
The post response variable says to take the vmware-api-session-id
response header value and save it in a variable for future use, like in our next request.
My second request I named ‘Get VM’ and is a GET of https://{{VC}}/sdk/vim25/{{vcVersion}}/VirtualMachine/vm-31/config
, where vm-31
is the managed object reference ID of a specific VM. For this request, I’ve set two headers, the content-type=application/json
and vmware-api-session-id={{vmare-api-session-id}}
, which uses the variable we retrieved from the login request as shown below:
With these two headers defined, we can send our request, and it will retrieve the configuration details of our specific VM.
If there is another request we need to make in this same collection, we can right click the name of our request (Get VM in this case) and clone the request. This will make a new request with the same customized values already populated. This allows us to simply change the URL and submit a different request. For example, if I want to get details about all my license keys, I can change the URL to https://{{VC}}/sdk/vim25/{{vcVersion}}/LicenseManager/LicenseManager/licenses
. The headers are already populated so I can send the request (CTRL + Enter is the default key binding for this task) and we’ll have a JSON body showing all of our license keys.
Example 3: Aria Ops Casa API
Finally, in another previous post we looked at logging into the Aria Operations Casa API using an LDAP account. This is a bit more difficult as we needed to base64 encode a username:password string to pass as a header for authentication. Lets see if we can do the same in Bruno.
- Create new collection
Aria Ops Casa
- Create new request
Casa Login
- Create new environment
lab
with three variables:vropsServer
,vropsCasaLdapUser
,vropsCasaLdapPass
and enter appropriate values. For the password I checked the ‘secret’ checkbox. - For the request type we’ll select
POST
and for our URL we will enterhttps://{{vropsServer}}/casa/authorize
- On the script tab, we’ll build a ‘pre request’ to do some of the heavy lifting for authentication. Specifically, we’ll use a built-in function to do base64 encoding of our username/password string and then set our request Authorization header using that string. Sample code below:
const btoa = require("btoa");
var b64login = "vrops-ldap " + btoa(bru.getEnvVar("vropsCasaLdapUser")+":"+bru.getEnvVar("vropsCasaLdapPass"));
req.setHeader("Authorization", b64login );
- On the
Vars
tab we’ll update the post response section to create a new variable namedaccessToken
and use the expressionres.body.accessToken
to get theaccessToken
property from the body of the response.
Running the above request should get our server name, username, and password variables and use them to connect to the API. We’ll then create a new variable with the token we need for future requests.
To check the Aria Operations cluster status, we’ll start a new API request. This request must run after the above request, which populates the accessToken variable.
- Request Name:
Get Cluster Status
- URL:
https://{{vropsServer}}/casa/sysadmin/cluster/online_state
- Headers: Name=
Authorization
Value=Bearer {{accessToken}}
- This request should return a JSON body showing our current Aria Operations cluster state.
We now have this collection saved so we can easily access it in the future. If we have additional Aria Operations instances, we can copy the environments (so that all the variable names come over) and then update the variable values accordingly. This gives us a quick drop down to select which Aria Operations environment to query so we don’t need to re-enter username & passwords every time.
Conclusion
Bruno makes quick work of firing off a simple API call. The collections and environments are useful, especially when we have many endpoints we may want to query. I can see including this application as part of my API toolkit and you should consider it too. More information about Bruno can be found in the official docs at https://docs.usebruno.com/introduction/what-is-bruno.