{"id":2134,"date":"2024-12-18T13:11:00","date_gmt":"2024-12-18T18:11:00","guid":{"rendered":"https:\/\/enterpriseadmins.org\/blog\/?p=2134"},"modified":"2024-12-19T08:07:26","modified_gmt":"2024-12-19T13:07:26","slug":"getting-started-with-bruno-a-beginners-guide-to-simplifying-api-interactions","status":"publish","type":"post","link":"https:\/\/enterpriseadmins.org\/blog\/scripting\/getting-started-with-bruno-a-beginners-guide-to-simplifying-api-interactions\/","title":{"rendered":"Getting Started with Bruno: A Beginner\u2019s Guide to Simplifying API Interactions"},"content":{"rendered":"\n<p>I&#8217;ve recently been working with APIs more than ever. A colleague recently introduced me to <a href=\"https:\/\/www.usebruno.com\/\">Bruno<\/a>, an offline, open-source API client. For the most part, I had been interacting with APIs using swagger UIs in product or with PowerShell&#8217;s <code>Invoke-RestMethod<\/code>.  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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example 1: APC BackUPS<\/h2>\n\n\n\n<p>In a <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/leveraging-vmware-aria-operations-for-power-consumption-tracking\/\" data-type=\"link\" data-id=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/leveraging-vmware-aria-operations-for-power-consumption-tracking\/\">previous article<\/a> we explored creating our own custom &#8216;API&#8217; 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.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create new collection<\/li>\n\n\n\n<li>Create new request\n<ul class=\"wp-block-list\">\n<li>Name the request (<code>getStats<\/code>)<\/li>\n\n\n\n<li>Specify the URL (<code>http:\/\/servername.example.com:6545\/getStats<\/code>)<\/li>\n\n\n\n<li>Run (the right arrow at the end of the URL).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>The response should show our JSON body that we crafted in our script.  Now if I need to run this again, I don&#8217;t have to remember the hostname\/port number for my service, I can just hit go and get the current response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example 1b: Using a Bruno &#8216;environment&#8217;<\/h2>\n\n\n\n<p>In the top right of a Bruno collection, there is a drop down list that says &#8216;No Environment&#8217;.  If we select that drop down and &#8216;Configure&#8217; we can create a new environment.  This environment is a place to store variables, like server names, ports, and credentials.  For my example, I&#8217;m going to create an environment named &#8216;server room&#8217;.  In the &#8216;server room&#8217; environment, I&#8217;ll define a variable named <code>apcBackupsHost<\/code> with the value <a href=\"http:\/\/servername.example.com:6545\/getStats\"><code>servername.example.com<\/code><\/a>).  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:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"383\" height=\"47\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-3.png\" alt=\"\" class=\"wp-image-2138\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-3.png 383w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-3-300x37.png 300w\" sizes=\"auto, (max-width: 383px) 100vw, 383px\" \/><\/a><\/figure>\n\n\n\n<p>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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example 2: VI\/JSON<\/h2>\n\n\n\n<p>The next example comes from a prior post as well &#8212; <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/using-vi-json-with-powershell\/\">Using VI\/JSON with Powershell<\/a>.  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&#8217;ll make a new collection with a POST request to login.  We&#8217;ll also make an environment for this collection that has four variables:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>VC <\/em>= vCenter Server name or IP<\/li>\n\n\n\n<li><em>vcVersion <\/em>= the version used in our request (8.0.3.0 in my case)<\/li>\n\n\n\n<li><em>username <\/em>= the username used to connect to vCenter Server<\/li>\n\n\n\n<li><em>password <\/em>= the password used to connect to vCenter Server.<\/li>\n<\/ul>\n\n\n\n<p>I&#8217;ve named my request &#8216;Login&#8217; and set a few properties.  First the URL is <a href=\"https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/SessionManager\/SessionManager\/Login\"><code>https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/SessionManager\/SessionManager\/Login<\/code><\/a>, which contains two of the variables from my environment.  The <code>body<\/code> of the login contains the other two variables, as pictured below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"672\" height=\"240\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-5.png\" alt=\"\" class=\"wp-image-2142\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-5.png 672w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-5-300x107.png 300w\" sizes=\"auto, (max-width: 672px) 100vw, 672px\" \/><\/a><\/figure>\n\n\n\n<p>In addition to the Body I&#8217;ve made two other tweaks to this request.  You can see where tweaks have been made in the above screenshot&#8230; any tab with a change has an indicator count.  I&#8217;ve outlined the specific changes below:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span style=\"text-decoration: underline;\">Headers<\/span>: \n<ul class=\"wp-block-list\">\n<li>    Name = <code>Content-Type<\/code> \n<ul class=\"wp-block-list\">\n<li>    Value = <code>application\/json<\/code><\/li>\n\n\n\n<li><span style=\"text-decoration: underline;\">Vars > Post Response:<\/span> <\/li>\n\n\n\n<li>     Name: <code>vmware-api-session-id<\/code> \n<ul class=\"wp-block-list\">\n<li>     Expr: <code>res.headers['vmware-api-session-id']<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>The post response variable says to take the <code>vmware-api-session-id<\/code> response header value and save it in a variable for future use, like in our next request.<\/p>\n\n\n\n<p>My second request I named &#8216;Get VM&#8217; and is a GET of <a href=\"https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/VirtualMachine\/vm-31\/config\"><code>https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/VirtualMachine\/vm-31\/config<\/code><\/a>, where <code>vm-31<\/code> is the managed object reference ID of a specific VM.  For this request, I&#8217;ve set two headers, the <code>content-type=application\/json<\/code> and <code>vmware-api-session-id={{vmare-api-session-id}}<\/code>, which uses the variable we retrieved from the login request as shown below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"609\" height=\"247\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-7.png\" alt=\"\" class=\"wp-image-2144\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-7.png 609w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-7-300x122.png 300w\" sizes=\"auto, (max-width: 609px) 100vw, 609px\" \/><\/a><\/figure>\n\n\n\n<p>With these two headers defined, we can send our request, and it will retrieve the configuration details of our specific VM.<\/p>\n\n\n\n<p>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 <a href=\"https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/LicenseManager\/LicenseManager\/licenses\"><code>https:\/\/{{VC}}\/sdk\/vim25\/{{vcVersion}}\/LicenseManager\/LicenseManager\/licenses<\/code><\/a>.  The headers are already populated so I can send the request (CTRL + Enter is the default key binding for this task) and we&#8217;ll have a JSON body showing all of our license keys.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example 3: Aria Ops Casa API<\/h2>\n\n\n\n<p>Finally, in <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/automating-cluster-management-with-aria-operations-api\/\">another previous post<\/a> 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.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create new collection <code>Aria Ops Casa<\/code><\/li>\n\n\n\n<li>Create new request <code>Casa Login<\/code><\/li>\n\n\n\n<li>Create new environment <code>lab<\/code> with three variables: <code>vropsServer<\/code>, <code>vropsCasaLdapUser<\/code>, <code>vropsCasaLdapPass<\/code> and enter appropriate values.  For the password I checked the &#8216;secret&#8217; checkbox.<\/li>\n\n\n\n<li>For the request type we&#8217;ll select <code>POST<\/code> and for our URL we will enter <a href=\"https:\/\/{{vropsServer}}\/casa\/authorize\"><code>https:\/\/{{vropsServer}}\/casa\/authorize<\/code><\/a><\/li>\n\n\n\n<li>On the script tab, we&#8217;ll build a &#8216;pre request&#8217; to do some of the heavy lifting for authentication. Specifically, we&#8217;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:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>const btoa = require(\"btoa\");\nvar b64login = \"vrops-ldap \" + btoa(bru.getEnvVar(\"vropsCasaLdapUser\")+\":\"+bru.getEnvVar(\"vropsCasaLdapPass\"));\nreq.setHeader(\"Authorization\", b64login );<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>On the <code>Vars<\/code> tab we&#8217;ll update the post response section to create a new variable named <code>accessToken<\/code> and use the expression <code>res.body.accessToken<\/code> to get the <code>accessToken<\/code> property from the body of the response.<\/li>\n<\/ul>\n\n\n\n<p>Running the above request should get our server name, username, and password variables and use them to connect to the API. We&#8217;ll then create a new variable with the token we need for future requests.<\/p>\n\n\n\n<p>To check the Aria Operations cluster status, we&#8217;ll start a new API request.  This request must run after the above request, which populates the accessToken variable.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Request Name: <code>Get Cluster Status<\/code><\/li>\n\n\n\n<li>URL: <a href=\"https:\/\/{{vropsServer}}\/casa\/sysadmin\/cluster\/online_state\"><code>https:\/\/{{vropsServer}}\/casa\/sysadmin\/cluster\/online_state<\/code><\/a><\/li>\n\n\n\n<li>Headers: Name=<code>Authorization<\/code> Value=<code>Bearer {{accessToken}}<\/code><\/li>\n\n\n\n<li>This request should return a JSON body showing our current Aria Operations cluster state.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"716\" height=\"186\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-4.png\" alt=\"\" class=\"wp-image-2140\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-4.png 716w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2024\/11\/image-4-300x78.png 300w\" sizes=\"auto, (max-width: 716px) 100vw, 716px\" \/><\/a><\/figure>\n\n\n\n<p>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&#8217;t need to re-enter username &amp; passwords every time.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>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 <a href=\"https:\/\/docs.usebruno.com\/introduction\/what-is-bruno\">https:\/\/docs.usebruno.com\/introduction\/what-is-bruno<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;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&#8217;s Invoke-RestMethod. &hellip; <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/getting-started-with-bruno-a-beginners-guide-to-simplifying-api-interactions\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[9,3],"tags":[],"class_list":["post-2134","post","type-post","status-publish","format-standard","hentry","category-lab-infrastructure","category-scripting"],"_links":{"self":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2134","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/comments?post=2134"}],"version-history":[{"count":10,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2134\/revisions"}],"predecessor-version":[{"id":2158,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2134\/revisions\/2158"}],"wp:attachment":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/media?parent=2134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/categories?post=2134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/tags?post=2134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}