Showing posts with label rest. Show all posts
Showing posts with label rest. Show all posts

How I Learn APIs Quickly Using VS Code REST Client

When I stepped into a role with many APIs to ramp up on, I needed a faster way to explore endpoints, chain calls, and inspect responses without bouncing between tools.

The VS Code extension REST Client (humao.rest-client) helped me do exactly that. It lets you send REST and GraphQL requests directly from .http files in VS Code.

In this post, I will share the workflow I use to learn APIs quickly: keep requests close to code and notes, reuse values across calls, and avoid unnecessary copy-paste.

To start, create a .http file and add your requests there. Separate requests with ###; that separator is what makes the Send Request link appear above each request block.

Quick look at a page

Yes, you can use AI or tools like Postman and Insomnia to generate requests. But when your goal is to learn an API, it helps to keep everything close to your code and notes. I already had VS Code open all day, so I decided to keep my full API learning workflow there.

In my case, every API call is secured and requires an access token, so generating one is step zero. I could paste one token at the top of the file, but that approach breaks quickly: tokens expire, and pasted values can accidentally be committed or pushed.

That is why I keep credentials in a local .env file (for example: CLIENT_ID, CLIENT_SECRET, and TENANT_ID) that is listed in .gitignore.

Then I created my first request to generate an access token from those values:

POST https://login.microsoftonline.com:443/{{$dotenv TENANT_ID}}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={{$dotenv CLIENT_ID}}&scope={{$dotenv CREDS_SCOPES}}&client_secret={{$dotenv CLIENT_SECRET}}&grant_type=client_credentials

Quick breakdown: each {{$dotenv ...}} expression reads a value from your .env file, so you keep secrets out of your .http file and out of source control.

  • {{$dotenv TENANT_ID}} -> TENANT_ID
  • {{$dotenv CLIENT_ID}} -> CLIENT_ID
  • {{$dotenv CLIENT_SECRET}} -> CLIENT_SECRET

To execute the call, click the Send Request link above the request. The response opens in a new tab, and you can inspect the access token in the body.

Send Query button

Then you can use that token in the next request:

GET https://ecostruxure-building-platform-api-uat.se.app/api/Sites
Authorization: Bearer {{ACCESS_TOKEN}}
X-Api-Version: {{apiVersion}}

To avoid copy-pasting values, the better approach is to name requests and reference their responses as variables:

### ==============================
### Create Token
# @name createToken

POST https://login.microsoftonline.com:443/{{$dotenv TENANT_ID}}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={{$dotenv CLIENT_ID}}&scope={{$dotenv CREDS_SCOPES}}&client_secret={{$dotenv CLIENT_SECRET}}&grant_type=client_credentials

Notice the @name createToken labels that request. After it runs, you can access fields from its response body. The response content is JSON and looks like this:

{
  "token_type": "Bearer",
  "expires_in": 3599,
  "ext_expires_in": 3599,
  "access_token": "eyJ0..."
}

For example, to retrieve the access_token value, we can use the expression createToken.response.body.$.access_token. When assigned to a variable, it looks like this:

@ACCESS_TOKEN={{createToken.response.body.$.access_token}}

Then you can use {{ACCESS_TOKEN}} in all your requests, like this one that retrieves all buildings:

### ==============================
#### Retrieve my sites
# @name getBuildings

GET https://ecostruxure-building-platform-api-uat.se.app/api/Buildings
Authorization: Bearer {{ACCESS_TOKEN}}
X-Api-Version: {{apiVersion}}

If you come back later and the token has expired, just run the token request again, and the variable {{ACCESS_TOKEN}} will automatically update for all subsequent requests. No copy-pasting required.

Extracting a value from a list response

What if a response returns multiple items and you need one specific ID? For example, the previous query returns multiple buildings, but I was interested in the building named "Virtual Building FB". The response looks like this:

[
  {
    "organizationName": "BDP Team",
    "organizationId": "2dd6da1e",
    "siteId": "34580992",
    "floorCount": 1,
    "spaceCount": 2,
    "deviceCount": 0,
    "measurementCount": 0,
    "includesDeviceAndMeasurementCounts": false,
    "name": "Frank Demo Office",
    "referenceId": "frank-demo-building",
    "area": 0.0,
    "metadata": [],
    "id": "4a69071c"
  },
  {
    "organizationName": "BDP Team",
    "organizationId": "2dd6da1e",
    "siteId": "34580992",
    "floorCount": 2,
    "spaceCount": 5,
    "deviceCount": 0,
    "measurementCount": 0,
    "includesDeviceAndMeasurementCounts": false,
    "name": "Virtual Building FB",
    "referenceId": "vir-fb",
    "area": 0.0,
    "metadata": [
      {
        "name": "location",
        "value": "north wing"
      }
    ],
    "id": "01ab96fc"
  }
]

To get the value of the id property for one building with a specific name, we can filter with JSONPath:

@buildingId={{getBuildings.response.body.$[?(@.name=='Virtual Building FB')].id}}

This uses the previous request response (getBuildings) and extracts the matching id.

ℹ️ NOTE: If this is your first time seeing JSONPath, read it like this:

  • $ means "start from the root of the response body."
  • [?()] applies a filter.
  • @.name=='Virtual Building FB' keeps only objects where name matches.
  • .id returns the id field from the matched object.

Then use {{buildingId}} in the next request:

### ==============================
### Retrieve all floors within a specific building
# @name getFloors

GET https://ecostruxure-building-platform-api-uat.se.app/api/Buildings/{{buildingId}}/Floors
Authorization: Bearer {{ACCESS_TOKEN}}
X-Api-Version: {{apiVersion}}

Dynamic variables

Other built-in dynamic variables include:

  • {{$guid}}
  • {{$randomInt min max}}
  • {{$timestamp [offset option]}}
  • {{$datetime rfc1123|iso8601 [offset option]}}
  • {{$localDatetime rfc1123|iso8601 [offset option]}}
  • {{$processEnv [%]envVarName}}
  • {{$dotenv [%]variableName}}
  • {{$aadToken [new] [public|cn|de|us|ppe] [<domain|tenantId>] [aud:<domain|tenantId>]}}

For one historical-data query, I needed to pass a datetime in a very specific format. I solved that by generating the value with a dynamic variable:

@currentTimestamp={{$datetime 'YYYY-MM-DDTHH:mm:ss.SSS[000][Z]' -5 h}}

Then I passed {{currentTimestamp}} into the next request parameter.

Calling GraphQL from REST Client

Most examples above use GET requests, but you can also send POST requests and GraphQL queries.

For example, to get a building with its levels and rooms:

### GRAPH: buildings & equipment
POST https://ecostruxure-building-platform-api-uat.se.app/graphql
Content-Type: application/json
Authorization: {{ACCESS_TOKEN}}
X-REQUEST-TYPE: GraphQL
X-Api-Version: {{apiVersion}}

query MyQuery {
  buildings(where: {name: {eq: "Frank Demo Office"}}) {
    id
    name
    levels {
      name
      rooms {
        name
      }
    }
  }
}

Here we use POST because the GraphQL query is sent in the request body.

The response looks like:

{
  "data": {
    "buildings": [
      {
        "id": "4a69071c",
        "name": "Frank Demo Office",
        "levels": [
          {
            "name": "Ground Floor",
            "rooms": [
              {
                "name": "Open Office Space"
              },
              {
                "name": "Terrasse"
              }
            ]
          }
        ]
      }
    ]
  }
}

GraphQL is powerful here because you can request related data in one call instead of chaining multiple REST endpoints.

In short: if you are learning a new API, REST Client helps you move faster with less context switching. Keep your requests in a .http file, reuse values with @name + {{...}}, and iterate directly in VS Code.

Lately, this workflow has been even more useful as my day-to-day work includes broader platform discussions and faster discovery cycles.

If useful, I can share a follow-up .http starter template that you can adapt to your own APIs.

Show Me

You prefer watching a video? I got you here a video I did sharing the how I use REST Client extension.

Useful references:

Reading Notes #703

Reading Notes #703

I spent some time this week tracking new developments in the AI and DevOps space to see what was worth a closer look. These notes highlight several interesting pieces regarding new model releases, infrastructure shifts, and more efficient ways to manage your project files.



DevOps

AI

  • Introducing North Mini Code: Cohere’s First Model For Developers (Cohere Code Agents Team) - I feel like those new model blog posts are never simple to read. They are packed with numbers, statistics and comparisons with things you may not have known before. But that's what makes them interesting; they are a deep source of information. And yes, that model looks great!

  • Your API is Already an MCP Server (Marin Pavelić) - Very interesting idea that could make all of us save time and money.

Programming

Reading Notes #591

It is time to share new reading notes. It is a habit I started a long time ago where I share a list of all the articles, blog posts, and books that catch my interest during the week.

If you think you may have interesting content, share it!

Suggestion of the week

Cloud

Programming

~ Frank

Reading Notes #482


Another Monday, a new reading notes; a list of all the articles, blog posts, and books that catch my interest during the week and that I found interesting. It's a mix of the actuality and what I consumed.

You think you may have interesting content, share it!

Cloud

Programming

Podcast


~frank


Reading Notes #474



Every Monday (or Tuesday when the previous was a holiday 😏), I share my "reading notes". Those are a curated list of all the articles, blog posts, podcast episodes, and books that catch my interest during the week and that I found interesting. It's a mix of the actuality and what I consumed.

You think you may have interesting content, share it!

Cloud


Programming


Podcasts

  • How to Show More Grit at Work (Modern Mentor) - New to remote work (aka pandemic forced to work remotely) you still need to make your work visible and this episode could help you to get started.

Miscellaneous


~frank


Reading Notes #465


Every Monday, I share my "reading notes". Those are a curated list of all the articles, blog posts, podcast episodes, and books that catch my interest during the week and that I found interesting. It's a mix of the actuality and what I consumed.

You think you may have interesting content, share it!

Cloud

Programming

Miscellaneous


~Frank

Reading Notes #460

DevRel 2021 pass

Cloud


Programming


Podcast

  • #188 – Fame, Focus, and Billions of Pageviews with Evan Britton of Famous Birthdays (Indie Hackers) - A nice story where people focus on the users, worked super hard (and continue to do it), and had amazing success.

  • How to Stop Being Complacent (Influencer Entrepreneurs with Jenny Melrose) - A nice episode to "kick our butt" and get back on track. Yes, 2020 indeed brought tons of new challenges at all and every level. However. we must try to make this year better.

  • Who Owns Open-Source Software? (Coding Blocks) - Great discussion. Most of us, at some point, have to ask ourselves those questions (at least I know I did). It was very interesting listening to this episode and follow their thoughts.

  • 631 - How to Explain a Gap in Your Résumé (Modern Mentor) - I have gaps in my resumé and I always been very comfortable about it. When I saw the title of this episode I thought maybe I should be concerned... Happy to know I was right!


Miscellaneous


Books


Beyond the Trees: A Journey Alone Across Canada's Arctic

Author: Adam Shoalts

Nice adventure. I wish I could see all those images, animals, and horizon. I had a good time reading this odyssey. And for the record, as a canoeist/ kayaker I was impressed by the upriver challenge.












~Frank

Reading Notes #384

Programming

  • Install WSL 2 on Windows 10 (Thomas Maurer) - Awesome tutorial. If like me you didn't want to wait until the next Windows release or take the time to compile and debug a deployment....this tutorial is for us!

Databases


Miscellaneous

~

The Journey of an Azure SDK upgrade

JourneyRecently, with my team, we needed to upgrade a web solution to Azure SDK from 2.4 to 2.5.1. The upgrade was much longer and complex then expected, so I decide to share what we learn so maybe other could benefit from our experience.

The problem


The upgrade of the code and library was not really a problem. The documentation is available on MSDN and it's easy to follow. Few breaking changes were part of the version 2.5. One is the reason of this post: Diagnostics configuration must be applied separately after deployment[...]

Continue reading full article here



~Frank


The making of: Franky's Notes Azure Search - part 1


For a long time now, I'm thinking about creating an API that will allow to search easily through my notes. When Azure Search came public few weeks ago, I knew it was what this project needed to come alive. In this post, I will share how I did it, and more importantly, show how incredibly easy it was to do.


What's Azure Search?


Currently in preview, Azure Search is a cloud-based search-as-a-service that provides a set of REST APIs defined in terms of HTTP requests and responses, in OData JSON format.

Getting Started


From the Azure Portal, let's create an Azure Search Service by clicking the plus button on the bottom left of the screen. Select the Search option, and fill-up the options.

Azure_portal_crete_Search_Service_2014-10-20_0931

Application to populate my Azure Search service


First, we will need some data. My weekly posts Reading Notes are generated with a Ruby script that I did few years ago. You can read more about it on First step with Ruby: Kindle Clipping Extractor. Basically, the script extracts my notes from my Kindle and build a collection of notes grouped in different categories to generate a markdown file. That can easily be done by adding a new Json output file. Here is a quick view this output.
{
  "json_class": "FrankyNotes",
  "categories": {
    "dev": [
      {
        "id": 77077357,
        "title": "Customize the MVC 5 Application Users’ using ASP.Net Identity 2.0",
        "author": "Dhananjay kumar",
        "url": "http://debugmode.net/2014/10/01/customize-the-mvc-5-application-users-using-asp-net-identity-2-0/",
        "note": "Need to get the fukk article",
        "tags": "dev,frankysnotes,readingnotes160",
        "date": "2014/10/17",
        "category": "dev"
      },
      {
        "id": 77156372,
        "title": "Custom Login Scopes, Single Sign-On, new ASP.NET Web API – updates to 
      [...]

Now that we have some data, we need to create an index and be able to add document in it. A console application will be perfect for this job. At the time of writing this post, two libraries exist to interact with the Microsoft Azure Search REST API. For this part of the project, we will use the RedDog.Search library available on Github, since it's a .Net library.

Note: To create an index or upload documents you will need an admin key.

Admin_Key

First, we need to create an Index. Let's keep it simple and just create the index with all the properties of the json object. Here the code of my function CreateNoteIndex.
public IndexManagementClient Client
{
    get
    {
        if (_client == null){
            _client = new IndexManagementClient(ApiConnection.Create("frankysnotes", "AdminKey"));
        }
        return _client;
    }
}

public async Task<string> CreateNoteIndex()
{
    var createResult = await Client.CreateIndexAsync(new Index("notes")
        .WithStringField("id", opt => opt.IsKey().IsRetrievable())
        .WithStringField("title", opt => opt.IsRetrievable().IsSearchable())
        .WithStringField("author", opt => opt.IsRetrievable().IsSearchable())
        .WithStringField("url", opt => opt.IsRetrievable().IsSearchable(false))
        .WithStringField("note", opt => opt.IsRetrievable().IsSearchable())
        .WithStringField("tags", opt => opt.IsRetrievable().IsFilterable().IsSearchable())
        .WithStringField("date", opt => opt.IsRetrievable().IsSearchable())
        .WithStringField("category", opt => opt.IsRetrievable().IsFilterable().IsSearchable())
        );
    if (createResult.IsSuccess)
    {
        return "Index Reseted successfully";
    }
}

To be able to search by note instead of by post, I decided to break down the file in multiple documents containing one note by document. After what, it was really easy to upload the documents into the index.
public async Task<string> AddNotes(string filepath)
{
    var docs = new List<IndexOperation>();
    FrankysNotes notes = DeserializeFNotes(filepath);

    foreach (var category in notes.categories)
    {
        foreach (var fNote in notes.categories[category])
        {
            var doc = ConvertfNote(fNote);
            docs.Add(doc);
        }
    }

    var result = await Client.PopulateAsync("notes", docs.ToArray<IndexOperation>());

    return "File uploaded successfully";
}


private FrankysNotes DeserializeFNotes(string filepath)
{
    var jsonStr = File.ReadAllText(filepath);
    var serializer = new JavaScriptSerializer();

    var notes = serializer.Deserialize<FrankysNotes>(jsonStr);
    return notes;
}

private IndexOperation ConvertfNote(FrankysNote fnote)
{
    var doc = new IndexOperation(IndexOperationType.Upload, "id", fnote.id)
                    .WithProperty("title", fnote.title)
                    .WithProperty("author", fnote.author)
                    .WithProperty("url", fnote.url)
                    .WithProperty("note", fnote.note)
                    .WithProperty("tags", fnote.tags)
                    .WithProperty("date", fnote.date)
                    .WithProperty("category", fnote.category);
    return doc;
}

To keep the code as clear as possible, I removed all validations and error management. The json file is deserialized, then looping through all notes I build a list of IndexOperation. And Finally I upload all the notes with Client.PopulateAsync("notes", docs.ToArray<IndexOperation>());

Wrapping up


Using the RedDog.Search library to push documents in Azure Search Index was extremely easy. In fact, it's that simplicity that pushed me to share my discovery. In the next part of the series, I will create a simple HTML page to do real query.

Stay tune...

~ Frank Boucher

References