Quick Links

Using REST APIs to extend your scripts is a useful feature to implement. You can gain access to new functionalities, and the possibilities to create new more advanced scripts expand.

But the experience for many when starting to use REST APIs in scripts is that it feels quite clumsy and unnatural. In this post we discuss:

  • What a REST API is
  • How to read the most common form of documentation
  • How to use a REST API with PowerShell
  • Some tips and tricks on how to make it an easier and better experience

What Is REST?

REST, or RESTful APIs, is an API that uses HTTP requests to fetch, add, delete, or manipulate data in different services.

What we want to do with the data is usually decided by what HTTP method that you use. Here is a summarized list of HTTP methods and what they are used to do in a REST API:

  • GET---Read
  • POST---Create
  • PATCH---Partial update/modify
  • PUT---Update/replace
  • DELETE---Remove

The data returned by a REST API is usually returned in JSON format.

Now, let's get started with our first API call!

Reading the Docs

Learning to read and interpret the documentation for different REST APIs is essential for using them. Luckily, if you know how to read one style of documentation, you can learn quickly how to read others.

We are using petstore.swagger.io in this article, as it uses the popular Swagger framework that's pretty common to encounter in the real world.

The most essential information about the REST API endpoints.

The previous picture shows the most essential information about the REST API endpoints:

  • HTTP method---GET/POST/DELETE, etc.
  • Relative URL to the REST API endpoint (Base URL is usually presented at the top of the documentation page)
  • A brief description

Getting into the Details

The first page of the documentation is great, and you can usually perform most calls that require the HTTP GET method with that information. But methods like POST and SET usually require that you click and expand the row to get more information.

If you click on one of the rows, you're presented with information that looks like this:

The REST endpoint that can create a new pet object.

Here, we've presented the REST endpoint that can create a new pet object. It specifies how the JSON should look that was supplied in the body of the POST and what type of content it accepts. Other REST endpoints specifies what it's different parameters are, what data type it should be, etc.

That's the basics for reading the documentation. Now that's clear, it's time to start using REST APIs with PowerShell.

GET(ting) Your First Data

Using REST APIs with PowerShell is usually pretty straightforward, and you're using built-in cmdlets so no extra modules are needed. You're going to fetch data by using the GET method on /pet/{petId} endpoint.

If you expand the /pet/{petId} endpoint in the documentation, you can see that {petId} is actually a parameter that takes an integer.

{petId} is actually a parameter that takes an integer.

That makes the URL for fetching the pet object with id 1: https://petstore.swagger.io/v2/pet/1

SWAGGER REST API documentation usually presents the base URL at the top of the page.

Now, let's get started with PowerShell. Open up a Terminal window and enter:

        PS51 > Invoke-RestMethod -Method GET -ContentType "application/json" -Uri "https://petstore.swagger.io/v2/pet/1"

id : 1
category : @{id=0; name=string}
name : doggie
photoUrls : {string}
tags : {@{id=0; name=string}}
status : available

Invoke-RestMethod converts the returned JSON automatically to an object, as the content type "application/json" is returned in the response from the server.

Error 404 - Not found usually means that the object can't be found, not that the URL is mistyped.

You have now successfully made your first REST API call. But only being able to GET data is pretty limiting, so let's create something with the POST method.

Creating an Object with the POST Method

The POST method is most commonly used to create, such as creating users or entries, etc. A POST request sends a BODY containing information to the REST endpoint, usually in JSON format, but it can also be as a URL-encoded form.

You're going to learn how to create a JSON object that you can POST to the /pet endpoint.

You can see how the JSON is supposed to look if you expand the POST /pet row in the documentation.

 How the JSON should look if you expanded the POST /pet row in the documentation.

Let's start out by creating a hashtable that we can later convert to a JSON object. Raw JSON should be avoided in PowerShell scripts becaise it's limiting its capabilities.

        $Body = @{
    id = 19
    category = @{
        id = 45
        name = "Whatever"
    }
    name = "Dawg"
    photoUrls = @(
        "string"
    )
    tags = @(
@{
            id = 0
            name = "string"
        }
    )
    status = "available"
}

If you're having a hard time creating a hashtable that converts to the JSON you want, install the PsdKit module and use the command: $JsonString | ConvertTo-Psd

You now have a hashtable that you can convert to a JSON string and POST to the /pet endpoint:

        $JsonBody = $Body | ConvertTo-Json
$Uri = "https://petstore.swagger.io/v2/pet"
Invoke-RestMethod -ContentType "application/json" -Uri $Uri -Method Post -Body $JsonBody

id : 19
category : @{id=45; name=Whatever}
name : Dawg
photoUrls : {string}
tags : {@{id=0; name=string}}
status : available

When the object is created, you usually receive the object that was created for confirmation.

Using DELETE

The DELETE method deletes data, and the way of doing that is pretty similar to the GET method as demonstrated here:

        PS51 > Invoke-RestMethod -Method DELETE -ContentType "application/json" -Uri "https://petstore.swagger.io/v2/pet/1"
    

Just be aware so you don't delete anything that you might need.

Using PUT

The PUT method updates already existing data. This is done similarly to the POST method, by submitting a full or partial JSON object:

        PS51> $Body = [PSCustomObject]@{
    id = 19
    name = "Dawg with a new name"
}

PS51> $JsonBody = $Body | ConvertTo-Json
PS51> $Uri = "https://petstore.swagger.io/v2/pet"
PS51> Invoke-RestMethod -ContentType "application/json" -Uri $Uri -Method PUT -Body $JsonBody

id name photoUrls tags
-- ---- --------- ----
19 Dawg with a new name {} {}

Usually, the REST API returns a JSON object with the used and/or updated data. You can see that the object was updated by using the GET method towards it:

        PS 51> Invoke-RestMethod -ContentType "application/json" -Uri "https://petstore.swagger.io/v2/pet/19"

id : 19
category : @{id=45; name=Whatever}
name : Dawg with a new name
photoUrls : {string}
tags : {@{id=0; name=string}}
status : available

Creating Functions

Writing out these commands as they are can become quite tedious and isn't really scalable. If we're calling an endpoint more than once, then create a function for it. It's pretty simple and only a few lines are needed:

        Function Get-PetstorePet {
    [cmdletbinding()]
    param(
        # Id of the pet
        [Parameter(Mandatory,ValueFromPipeline)]
        [int]$Id
    )
    Begin{}
    Process{
        $RestMethodParams = @{
            Uri = "https://petstore.swagger.io/v2/pet/$Id"
            ContentType = "application/json"
            Method = "GET"
        }
        Invoke-RestMethod @RestMethodParams
    }
    End{}
}

After creating your function, you can call it in your script:

        PS51> Get-PetstorePet -Id 1

id name photoUrls tags
-- ---- --------- ----
 1 Doggie {http://picture.url} {}

You can do this for the POST method as well for creating a new pet in the pet store:

        Function Add-PetstorePet {
    [cmdletbinding()]
    param(
        # Id of the pet
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [int]$Id,
        # Name of the pet
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$Name,
        # Status of the pet (available, sold etc)
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$Status,
        # Id of the pet category
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [int]$CategoryId,
        # Name of the pet category
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string]$CategoryName,
        # URLs to photos of the pet
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [string[]]$PhotoUrls,
        # Tags of the pets as hashtable array: @{Id=1;Name="Dog"}
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [Hashtable[]]$Tags
    )
    Begin{}
    Process{
        $Body = @{
            id = $Id
            category = @{
                id = $CategoryId
                name = $CategoryName
            }
            name = $Name
            photoUrls = $PhotoUrls
            tags = $Tags
            status = $Status
        }
        $BodyJson = $Body | ConvertTo-Json
        $RestMethodParams = @{
            Uri = "https://petstore.swagger.io/v2/pet/"
            ContentType = "application/json"
            Method = "Post"
            Body = $BodyJson
        }
        Invoke-RestMethod @RestMethodParams
    }
    End{}
}

And calling this PowerShell function afterward makes this quite long task a lot easier:

        PS51> $AddPetStorePetsParams = @{
    Id = 44
    Name = "Birdie"
    Status = "available"
    CategoryId = 50
    CategoryName = "Hawks"
    PhotoUrls = "https://images.contoso.com/hawk.jpg"
    Tags = @(
@{
            Id=10
            Name="Not eagles"
        }
    )
}
PS51> Add-PetStorePet @AddPetStorePetsParams

id : 44
category : @{id=50; name=Hawks}
name : Birdie
photoUrls : {https://images.contoso.com/hawk.jpg}
tags : {@{id=0}}
status : available

Chances are that many of the modules that you use daily are made of functions that only uses REST APIs in the background.

Summary

Learning how to use REST APIs is mainly about learning to read the documentation. We used SWAGGER-based documentation in this post, as it represents how other styles of documentation may look as well.

Also, converting your API calls to a function can save you a lot of time, make your work easier, and your scripts cleaner.