---
title: Building a Notion PowerShell Module: Part 3
description: Learn the next steps of building a PowerShell Notion module in this in-depth PowerShell tutorial by ATA Learning!
canonical: https://adamtheautomator.com/notion-powershell-3/
---

# Building a Notion PowerShell Module: Part 3

> Learn the next steps of building a PowerShell Notion module in this in-depth PowerShell tutorial by ATA Learning!

Source: https://adamtheautomator.com/notion-powershell-3/

---

- Building a Notion PowerShell Module: Part 3 Tap to hide Home

- Tutorials

- Guidebooks

- Instructors

- Get Paid to Write

- Advertising

- Recommended Resources

- About Adam

                Search for:

-

-

-

-

# Building a Notion PowerShell Module: Part 3

        Published:10 November 2023 - 4 min. read

- PowerShell

          [](https://adamtheautomator.com/author/alistek/)

            [Adam Listek](https://adamtheautomator.com/author/alistek/)

            Read [more tutorials](https://adamtheautomator.com/author/alistek/) by Adam Listek!

-

-

-

        [](https://specopssoft.com/product/specops-password-auditor/?utm_source=adamtheautomator&utm_medium=referral&utm_campaign=adamtheautomator_referral_na&utm_content=display)
        Audit Active Directory for stale users, weak passwords, and other security risks with [Specops Password Auditor](https://specopssoft.com/product/specops-password-auditor/?utm_source=adamtheautomator&utm_medium=referral&utm_campaign=adamtheautomator_referral_na&utm_content=text).

Table of Contents

- Prerequisites
- The Power of Notion DatabasesCreating a Page
- Creating the Database
- Creating a Page in the Database
- Adding to the Module
- Next Steps

                XFacebookLinkedIn

Building on the previous two Notion tutorials, you have learned how to create a Notion integration token and retrieve, update, and delete Notion blocks via PowerShell and the Notion API.

Even more powerful are Notion’s Databases. Relational databases help to organize data logically and provide opportunities for analysis. With Notion, store pages within Databases with additional properties. In this tutorial, learn how to create a page, database, and database item. Build upon the Notion PowerShell module to continue expanding functionality.

## Prerequisites

To follow along in this tutorial, you only need a Notion account and [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3); here, PowerShell v7.3.7 is in use.

## The Power of Notion Databases

Though you may think of pages as standalone, within Notion, they are also integral to Databases. Therefore, combining the Page operations with those of Databases helps to understand the linkage. In Notion, Databases may have properties of many different types, such as Selects, Checkboxes, or Numbers.

### Creating a Page

First, you need a place to put the database. To demonstrate this, [create a new page using the /pages endpoint](https://developers.notion.com/reference/post-page). The resulting id will be the GUID used as the parent_id of the new Database.

$APIKey     = 'secret_sK5Qir0UAgkLF1iauP5iMsq1vq3YW5S9o5GvF7M8PjK'
$APIURI     = 'https://api.notion.com/v1'
$APIVersion = '2022-06-28'
$GUID       = 'a2b3646d-e941-4df4-874d-56153139b618'

$Params = @{
    "Headers" = @{
        "Authorization"  = "Bearer {0}" -F $APIKey
        "Content-type"   = "application/json"
        "Notion-Version" = "{0}" -F $APIVersion
    }
    "Method"  = 'POST'
    "URI"     = ("{0}/pages" -F $APIURI, [GUID]::new($GUID))
    "Body"    = @{
        "parent" = @{
            "type"    = 'page_id'
            "page_id" = $GUID
        }
        "properties" = @{
            "title" = @(
                @{
                    "text" = @{"content" = "Test" }
                }
            )
        }
        "children" = @()
    } | ConvertTo-JSON -Depth 100
}

$Result = Invoke-RestMethod @Params

Result of creating a new page.

### Creating the Database

With the page newly created, you will use the page_id parent [with the /databases endpoint](https://developers.notion.com/reference/create-a-database) to create a new Database with a [single required Title](https://developers.notion.com/reference/page-property-values#title) property. There is a required title property that gives the Database its unique name as well.

$Params = @{
    "Headers" = @{
        "Authorization"  = "Bearer {0}" -F $APIKey
        "Content-type"   = "application/json"
        "Notion-Version" = "{0}" -F $APIVersion
    }
    "Method"  = 'POST'
    "URI"     = ("{0}/databases" -F $APIURI)
    "Body"    = @{
        "parent" = @{
            "type"    = "page_id"
            "page_id" = $Result.id
        }
        "title" = @(
            @{
                "type" = "text"
                "text" = @{
                  "content" = "Test"
                  "link" = $null
                }
            }
        )
        "properties" = @{
            "Name" = @{
                "type" = "title"
                "title" = @{}
            }
						"Checkbox" = @{
                "type" = "checkbox"
                "checkbox" = @{}
            }
            "Number" = @{
                "type" = "number"
                "number" = @{
                  "format" = "number_with_commas"
                }
            }
        }
    } | ConvertTo-Json -Depth 100
}

$Result = Invoke-RestMethod @Params

Result of creating a new database.

### Creating a Page in the Database

Finally, to store data within the Database, you must create a new item: a full Notion page. As the page item exists in the database, it has the added benefit of any additional properties. In the example above, you created a [checkbox](https://developers.notion.com/reference/property-object#checkbox) and [number](https://developers.notion.com/reference/property-object#number) property.

$Params = @{
    "Headers" = @{
        "Authorization"  = "Bearer {0}" -F $APIKey
        "Content-type"   = "application/json"
        "Notion-Version" = "{0}" -F $APIVersion
    }
    "Method"  = 'POST'
    "URI"     = ("{0}/pages" -F $APIURI)
    "Body"    = @{
        "parent" = @{
            "type"        = "database_id"
            "database_id" = $Result.id
        }
        "properties" = @{
            "Name" = @{
                "title" = @(
                    @{
                        "text" = @{"content" = "This is a Test Task"}
                    }
                )
             }
             "Checkbox" = @{
                "checkbox" = $True
             }
             "Number" = @{
                "number" = 1000
             }
        }
    } | ConvertTo-Json -Depth 100
}

$Result = Invoke-RestMethod @Params

Result of creating a new database item.

## Adding to the Module

To round out the module, three new functions will be created. These functions will be:

- New-NotionPage

- New-NotionDatabase

You may wonder why there isn’t a New-NotionDatabase. As technically you are calling the same endpoint, /pages, for both Databases and Pages, you can extend your New-NotionPage function to cover both use cases.

Introduced in this function is the [concept of parameter sets](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parameter_sets?view=powershell-7.3). Parameter sets offer a way to separate parameters into logical groupings and only allow those that belong together. Defined below is the DefaultParameterSetName within the CmdletBinding declaration. This gives a default for the function.

It’s necessary to define all the parameter sets each parameter belongs in. If a parameter must be in both, then multiple declarations must be used.

New-NotionPage

Function New-NotionPage {
    [CmdletBinding(SupportsShouldProcess = $True, DefaultParameterSetName = 'Page')]

    Param(
        [String]$APIKey,
        [String]$APIVersion,
        [ValidateScript( { [System.URI]::IsWellFormedUriString( $_ ,[System.UriKind]::Absolute ) } )][String]$APIURI,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Alias("ID")]
        [ValidateScript( { Try { If ( [GUID]::Parse( $_ ) ) { $True } } Catch { $False } } )][String]$GUID,

        [Parameter(Mandatory, ParameterSetName = 'Page')]
        $Title,

        $Content,

        [Parameter(ParameterSetName = 'Database')]
        $Properties,

        [Parameter(ParameterSetName = 'Database')]
        [Switch]$Database
    )

    Process {
        $Body = @{
            "parent"     = $Null
            "properties" = $Null
        }

        If ($Database) {
            $Body.properties = $Properties
            $Body.parent = @{
                "type"    = 'database_id'
                "database_id" = [GUID]::New($GUID)
            }
        } Else {
            $Body.parent = @{
                "type"    = 'page_id'
                "page_id" = [GUID]::New($GUID)
            }
            $Body.properties = @{
                "title" = @(
                    @{
                        "text" = @{ "content" = $Title }
                    }
                )
            }
        }

        If ($Content) {
            $Body.Add("children", $content)
        }

        Write-Verbose ($Body | Out-String)

        $Params = @{
            "Headers" = @{
                "Authorization"  = "Bearer {0}" -F $APIKey
                "Content-type"   = "application/json"
                "Notion-Version" = "{0}" -F $APIVersion
            }
            "Method" = 'POST'
            "URI"    = ("{0}/pages" -F $APIURI)
            "Body"   = $Body | ConvertTo-JSON -Depth 100
        }

        Write-Verbose "[Process] Params: $($Params | Out-String)"

        If ($PSCmdlet.ShouldProcess($GUID,"Adding Page")) {
            Try {
                $Result = Invoke-RestMethod @Params -ErrorAction 'Stop'
            } Catch {
                $Message = ($Error[0].ErrorDetails.Message | ConvertFrom-JSON).message

                Write-Error "Command Failed to Run: $Message"
            }

            If ($Result) {
                $Result
            }
        }
    }
}

New-NotionDatabase

Function New-NotionDatabase {
    [CmdletBinding(SupportsShouldProcess = $True)]

    Param(
      [String]$APIKey,
      [String]$APIVersion,
      [ValidateScript( { [System.URI]::IsWellFormedUriString( $_ ,[System.UriKind]::Absolute ) } )][String]$APIURI,

      [Parameter(ValueFromPipelineByPropertyName = $True)]
      [Alias("ID")]
      [ValidateScript( { Try { If ( [GUID]::Parse( $_ ) ) { $True } } Catch { $False } } )][String]$GUID,

      [Parameter(Mandatory)]
      $Title,

      $Properties
    )

    Process {
        $Params = @{
            "Headers" = @{
                "Authorization"  = "Bearer {0}" -F $APIKey
                "Content-type"   = "application/json"
                "Notion-Version" = "{0}" -F $APIVersion
            }
            "Method" = 'POST'
            "URI"    = ("{0}/databases" -F $APIURI)
            "Body"   = @{
                "parent" = @{
                    "type"    = 'page_id'
                    "page_id" = [GUID]::New($GUID)
                }
                "title" = @(
                    @{
                        "type" = "text"
                        "text" = @{
                          "content" = $Title
                          "link" = $Null
                        }
                    }
                )
                "properties" = $Properties
            } | ConvertTo-JSON -Depth 100
        }

        Write-Verbose "[Process] Params: $($Params | Out-String)"

        If ($PSCmdlet.ShouldProcess($GUID,"Adding Database")) {
            Try {
                $Result = Invoke-RestMethod @Params -ErrorAction 'Stop'
            } Catch {
                $Message = ($Error[0].ErrorDetails.Message | ConvertFrom-JSON).message

                Write-Error "Command Failed to Run: $Message"
            }

            If ($Result) {
                $Result
            }
        }
    }
}

When run together, you can see the results are the same as if you were to run the individual API calls, but in a more user-friendly nomenclature. By passing in the resulting objects from the previous calls you can simplify creation of child pages and objects.

$Parent = 'a2b3646de9414df4874d56153139b618'

$Result = New-NotionPage -GUID $Parent -Title "Database Page"

$Database = New-NotionDatabase -GUID $Result.id -Title "Test Database" -Properties @{
    "Name" = @{
        "type" = "title"
        "title" = @{}
    }
    "Checkbox" = @{
        "type" = "checkbox"
        "checkbox" = @{}
    }
    "Number" = @{
        "type" = "number"
        "number" = @{
          "format" = "number_with_commas"
        }
    }
}

$Item = New-NotionPage -Database -GUID $Database.id -Properties @{
    "Name" = @{
        "title" = @(
            @{
                "text" = @{"content" = "This is a Test Task"}
            }
        )
     }
     "Checkbox" = @{
        "checkbox" = $True
     }
     "Number" = @{
        "number" = 1000
     }
}

New containing page.

The full-page database.

Resulting database with subsequently added item.

## Next Steps

The next step is to add a few supporting functions to the module to create stronger typing of available block types and additional usability. The following few articles will wrap up the function and finish creating a usable and full-featured module!

  Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

  [Explore ATA Guidebooks](https://adamtheautomator.com/ata-guidebooks/)

## More from ATA Learning and Partners

- ### Recommended Resources! Recommended Resources for Training, Information Security, Automation, and more!

- ### Get Paid to Write! ATA Learning is always seeking instructors of all experience levels. Regardless if you’re a junior admin or system architect, you have something to share. Why not write on a platform with an existing audience and share your knowledge with the world?

- ### ATA Learning Guidebooks ATA Learning is known for its high-quality written tutorials in the form of blog posts. Support ATA Learning with ATA Guidebook PDF eBooks available offline and with no ads!

## Categories

- IT Ops

- Cloud

- DevOps

- Home Ops

- Information Security

- Software Development

## Site

- Home

- Tutorials

- Guidebooks

- Instructors

- Get Paid to Write

- Advertising

- Recommended Resources

- About Adam

        Copyright 2026&copy; ATA Learning | [Privacy Policy](https://adamtheautomator.com/privacy/)

                                                        Don't be left behind with the ATA Learning Newsletter!

Looks like you're offline!
