npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pageblade-sdk

v1.0.0

Published

PageBlade SDK API driven website hosting

Downloads

3

Readme

PageBlade SDK

PageBlade provide an API to manage websites, webpages, assets, visitor statistics, page version control, tenants and much more.

This Node.js / Typescript SDK provides an easy to use wrapper around all the available actions.

The REST API is directly accessible, though we strongly recommend using this SDK as it is maintained in alignment with all API changes providing strong typings for methods, entities and DTOs.

Contents

Installation

npm install pageblade-sdk --save

API Key

You can grab a free API key from the PageBlade website, simply signup with your Github account. Your key will allow evaluation of all features aside from custom domains and multiple tenants. Production ready plans are available for access to all features.

Quick Start

Here's a simple example to create your first website and webpage:

import { PageBlade } from 'pageblade-sdk';

// Initialise the SDK - free API key can be obtained from https://pageblade.com
const pageBlade = new PageBlade({ apiKey });

// Create a website using default values
const website = await pageBlade.createWebsite();

// Create and auto publish a page
const webpage = await pageBlade.createWebpage({
    name: 'Getting Started',
    slug: '/getting-started',
    websiteId: website.id,
    draftHtml: '<html><body>Hello PageBlade!</body></html>',
    publish: true,
});

console.log(webpage.publishedUrl);

Using default values, the create website method will generate a random subdomain. We've specified the publish: true property to make the page instantly available.

The created webpage can be accessed at the returned publishedUrl parameter.

All websites are hosted on a *.pageblade.site domain, unless you specify a custom domain, more on that later.

{
  publishedUrl: 'https://pinkcats.pageblade.site',
}

PageBlade Initialisation

The configuration object on the constructor is optional. Either specify your API key here or as the environment variable PAGEBLADE_API_KEY. If defaultTenantId is provided, it will be used for all requests unless overiden at request level where tenantId is often an optional parameter.

// Configuration is optional
const pageBlade = new PageBlade({
    apiKey,
    defaultTenantId,
});

What follows is a list of all available methods with examples.

API

Websites

Websites are the containers for webpages and assets and visitor tracking analytics.

The following DTO is used for creating and updating websites.

Type: WebsiteDto

| Property | Type | Default | Max Length | Notes | | :-- | :-- | :-- | :-- | :-- | | name | string? | My website| 150 | Name for your own reference | | description | string? | My website | 500 | Description for your own reference | | enabled | boolean? | true | - | Set to false to prevent the website from being publically accessible | | subdomain | string? | Random generated | 256 | subdomain.pageblade.site | | domain | string? | | 256 | Specify your own custom domain name |

Method: createWebsite(website?: WebsiteDto, tenantId?: string): Promise<WebsiteEntity>

Creates a new website with optional DTO parameters.

//
// Example: Create Website
//
const website = await pageBlade.createWebsite({
    name: 'My Amazing Website!',
    subdomain: 'amazing',
    domain: 'amazing.com',
});

console.log(website);

Example response:

// WebsiteEntity:
{
  description: 'My Website',
  domain: 'amazing.com',
  domainVerificationStatus: 'PENDING',
  domainVerificationKey: 'pageblade-AKXpx1Mrg7MGl4eGyswdP1NoykMkXTzJ',
  enabled: true,
  id: 'R6nF6teZdhde4Ex2ofHl2rnasGYtRe4X',
  name: 'My Amazing Website!',
  subdomain: 'amazing',
  hostedUrl: 'https://amazing.pageblade.site',
  cname: 'domain.pageblade.site',
  whenCreated: 1636211909820,
  whenUpdated: 1636211909820,
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
  whenDomainVerificationAttempted: null
}

In the example above we are specifying a custom domain name. For the domain to be correctly allocated to the website, the domain owner will need to create two entries in their DNS records:

  1. CNAME: Create a CNAME or ALIAS entry using the value provided in the cname property. This will point the domain to the PageBlade server.
  2. TXT: Create a TXT entry using the value provided in the domainVerificationKey property. PageBlade will periodically check this value to verify ownership. It is important this entry remains in the DNS otherwise it PageBlade will remove the domain allocation for the website without warning.

Method: updateWebsite(websiteId: string, website: WebsiteDto, tenantId?: string): Promise<WebsiteEntity>

Updates an existing website by passing the website ID and DTO.

//
// Example: Update Website
//
const website = await pageBlade.updateWebsite(
    websiteId,
    {
        name: 'A More Amazing Website!',
        domain: 'another-example.com',
    },
);

console.log(website);

Example response:

// WebsiteEntity:
{
  description: 'My Website',
  domain: 'another-example.com',
  domainVerificationStatus: 'PENDING',
  domainVerificationKey: 'pageblade-VWFMMQn0c6zxvE7b9ey2oANyuCd3RLj5',
  enabled: true,
  id: 'R6nF6teZdhde4Ex2ofHl2rnasGYtRe4X',
  name: 'A More Amazing Website!',
  subdomain: 'amazing',
  hostedUrl: 'https://amazing.pageblade.site',
  cname: 'domain.pageblade.site',
  whenCreated: 1636211909820,
  whenUpdated: 1636211909864,
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
  whenDomainVerificationAttempted: null
}

Method: duplicateWebsite(websiteId: string, tenantId?: string): Promise<WebsiteEntity>

Duplicates a website and all associated webpages and assets by passing the website ID. A new subdomain is randomly generated.

//
// Example: Duplicate Website
//
const duplicatedWebsite = await pageBlade.duplicateWebsite(websiteId);

console.log(duplicatedWebsite);

Example response:

// WebsiteEntity:
{
  description: 'My Website',
  domain: null,
  domainVerificationStatus: null,
  domainVerificationKey: null,
  enabled: true,
  id: 'uvyY0d6XEzujJCHajTQYl4l1XgqFnT7o',
  name: 'A More Amazing Website! copy',
  subdomain: 'minnesotamatrix',
  hostedUrl: 'https://minnesotamatrix.pageblade.site',
  cname: 'domain.pageblade.site',
  whenCreated: 1636211909898,
  whenUpdated: 1636211909898,
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
  whenDomainVerificationAttempted: null
}

Method: deleteWebsite(websiteId: string, tenantId?: string): Promise<void>

Deletes a website and all associated webpages, assets and tracking data by passing the website ID.

//
// Example: Delete Website
//
await pageBlade.deleteWebsite(websiteId);

Method: getWebsite(websiteId: string, tenantId?: string): Promise<WebsiteEntity>

Retrieves a single website for the passed website ID.

//
// Example: Get Website
//
const website = await pageBlade.getWebsite(websiteId);

console.log(website);

Example response:

// WebsiteEntity:
{
  description: 'My Website',
  domain: 'another-example.com',
  domainVerificationStatus: 'PENDING',
  domainVerificationKey: 'pageblade-VWFMMQn0c6zxvE7b9ey2oANyuCd3RLj5',
  enabled: true,
  id: 'R6nF6teZdhde4Ex2ofHl2rnasGYtRe4X',
  name: 'A More Amazing Website!',
  subdomain: 'amazing',
  hostedUrl: 'https://amazing.pageblade.site',
  cname: 'domain.pageblade.site',
  whenCreated: 1636211909820,
  whenUpdated: 1636211909864,
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
  whenDomainVerificationAttempted: null
}

Method: getWebsites(paginatedRequest: PaginatedWebsiteDto, tenantId?: string): Promise<PaginatedWebsiteEntity>

Retrieves a page of websites for the default or passed tenant ID.

//
// Example: Retrieve the first page of websites ordered by ascending creation date
//
const websites = await pageBlade.getWebsites({
    orderBy: 'whenCreated',
    orderDirection: ORDER_DIRECTION.ASC,
    pageIndex: 0,
    pageSize: 10,
});

console.log(websites);

Example response:

{
  nextPageUrl: null,
  totalRowsCount: 2,
  rows: [
    {
      description: 'My Website',
      domain: null,
      domainVerificationStatus: null,
      domainVerificationKey: null,
      enabled: true,
      id: 'lWg4LdrYUPWRcwJRuHzlxdvQ2Z7wHJhu',
      name: 'My New Website',
      subdomain: 'granularcambridgeshire',
      hostedUrl: 'https://granularcambridgeshire.pageblade.site',
      cname: null,
      whenCreated: 1636211908366,
      whenUpdated: 1636211908366,
      tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
      whenDomainVerificationAttempted: null
    },
    {
      description: 'My Website',
      domain: 'another-example.com',
      domainVerificationStatus: 'PENDING',
      domainVerificationKey: 'pageblade-VWFMMQn0c6zxvE7b9ey2oANyuCd3RLj5',
      enabled: true,
      id: 'R6nF6teZdhde4Ex2ofHl2rnasGYtRe4X',
      name: 'A More Amazing Website!',
      subdomain: 'amazing',
      hostedUrl: 'https://amazing.pageblade.site',
      cname: 'domain.pageblade.site',
      whenCreated: 1636211909820,
      whenUpdated: 1636211909864,
      tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB',
      whenDomainVerificationAttempted: null
    }
  ],
  totalPagesCount: 1,
  pageIndex: 0,
  pageSize: 10,
  orderBy: 'whenCreated'
}

Method: downloadWebsite(websiteId: string, tenantId?: string): Promise<DownloadEntity>

Returns a zip file in base64 format containing all pages and assets structured in alignment with the webpage slugs.

    //
    // Example: Download website to zip file format
    //
    const download = await pageBlade.downloadWebsite(websiteId);

    console.log(download);

Example response:

// DownloadEntity:
{
  filename: 'website_lWg4LdrYUPWRcwJRuHzlxdvQ2Z7wHJhu.zip',
  content: 'UEsDBBQACAAIAE96ZlMAAAAAAAAAA...',
  mimeType: 'application/zip'
}

Webpages

Each webpage belongs to a single Website. It must have a unique slug, with the default home page value being /

The following DTO is used for creating and updating webpages.

Types: WebpageCreateDto, WebpageUpdateDto (All properties optional)

| Property | Type | Default | Max Length | Notes | | :-- | :-- | :-- | :-- | :-- | | name | string | My webpage| 150 | Name for your own reference | | slug | string | / | 500 | Any valid slug path (valid chars???) | | websiteId | string | - | 32 | Required | | publish | boolean | false | - | Auto publishes the page | redirectUrl | string? | - | 500 | Any valid URL either relative or external | | draftHtml | string? | - | 1MB | Draft HTML content remains in draft until published | | previewHtml | string? | - | 1MB | Preview HTML is viewable immediately using the URL returned by the create or update method |

Method: createWebpage(webpage?: WebpageDto, tenantId?: string): Promise<WebpageEntity>

Creates a new webpage. name, slug and websiteId are required properties.

//
// Example: Create a draft version of a webpage
//
const webpage = await pageBlade.createWebpage({
    name: 'Home Page',
    slug: '/',
    websiteId: websiteId,
    draftHtml: '<html><body>Home</body></html>',
});

Example response:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: null,
  whenUpdated: 1636212524246,
  whenDraftSaved: 1636212524243,
  whenPreviewPublished: null,
  redirectUrl: null,
  publishedUrl: null,
  previewUrl: null,
  draftUrl: 'https://amazing.pageblade.site/?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: updateWebpage(webpageId: string, webpage: Webpageto): Promise<WebpageEntity>

Updates an existing webpage by passing the webpage ID and a DTO.

//
// Example: Update Webpage slug
//
const webpage = await pageBlade.updateWebpage(
    webpageId,
    {
        slug: '/moved-here',
    },
);

console.log(webpage);

Example response:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: null,
  whenUpdated: 1636212524289,
  whenDraftSaved: 1636212524243,
  whenPreviewPublished: null,
  redirectUrl: null,
  publishedUrl: null,
  previewUrl: null,
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Updates an existing webpage draft HTML.

//
// Example: Update Webpage draft HTML
//
const webpage = await pageBlade.updateWebpage(
    webpageId,
    {
        draftHtml: '<html><body>Home Updated</body></html>',
    },
);

console.log(webpage);

Example response, note the draftUrl property can be used to view the draft version:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: null,
  whenUpdated: 1636212524684,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: null,
  redirectUrl: null,
  publishedUrl: null,
  previewUrl: null,
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Updates an existing webpage preview HTML.

//
// Example: Update Webpage draft HTML
//
const webpage = await pageBlade.updateWebpage(
    webpageId,
    {
        previewHtml: '<html><body>Just checking to see how this looks!</body></html>',
    },
);

console.log(webpage);

Example response, note the previewUrl is now available:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: null,
  whenUpdated: 1636212525032,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: 1636212525030,
  redirectUrl: null,
  publishedUrl: null,
  previewUrl: 'https://amazing.pageblade.site/moved-here?eapY=preview',
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: publishDraft(webpageId: string, tenantId?: string): Promise<WebpageEntity>

Publishes the current draft version of the page.

Note: To view any previously published version of a page, append ?published={version} to the URL.

//
// Example: Publish draft version of webpage
//
const webpage = await pageBlade.publishDraft(webpageId);

console.log(webpage);

return webpage;

Example response, note the publishedUrl is now available and an entry in the publishedVersions array.

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: 1636212525070,
  whenUpdated: 1636212525318,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: 1636212525030,
  redirectUrl: null,
  publishedUrl: 'https://amazing.pageblade.site/moved-here',
  previewUrl: 'https://amazing.pageblade.site/moved-here?eapY=preview',
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [ 1636212525070 ],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: publishVersion(webpageId: string, version: number, tenantId?: string): Promise<WebpageEntity>

Publishes a previously published version of the page.

//
// Example: Publish previously published version of webpage
//
const webpage = await pageBlade.publishVersion(webpageId, version);

console.log(webpage);

Example response:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: 1636212525070,
  whenUpdated: 1636212525318,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: 1636212525030,
  redirectUrl: null,
  publishedUrl: 'https://amazing.pageblade.site/moved-here',
  previewUrl: 'https://amazing.pageblade.site/moved-here?eapY=preview',
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [ 1636212525070 ],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: duplicateWebpage(webpageId: string, tenantId?: string): Promise<WebpageEntity>

Duplicates a webpage by passing the webpage ID. The slug is appended to ensure unique.

//
// Example: Duplicate Webpage
//
const duplicatedWebpage = await pageBlade.duplicateWebpage(webpageId);

console.log(duplicatedWebpage);

return duplicatedWebpage;

Example response:

// WebpageEntity:
{
  id: 'kRIf6cNwrlI7CABuGwLAt6BwxMVQlCIQ',
  slug: '/moved-here-copy',
  name: 'Home Page copy',
  whenCreated: 1636212525516,
  whenPublished: null,
  whenUpdated: 1636212525516,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: 1636212525030,
  redirectUrl: null,
  publishedUrl: null,
  previewUrl: 'https://amazing.pageblade.site/moved-here-copy?6cNw=preview',
  draftUrl: 'https://amazing.pageblade.site/moved-here-copy?6cNw=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [ 1636212525070 ],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: deleteWebpage(webpageId: string, tenantId?: string): Promise<void>

Deletes a webpage by passing the webpage ID.

//
// Example: Delete Webpage
//
await pageBlade.deleteWebpage(webpageId);

Method: getWebpage(webpageId: string, tenantId?: string): Promise<WebpageEntity>

Retrieves a single webpage for the passed webpage ID.

//
// Example: Get Webpage
//
const webpage = await pageBlade.getWebpage(webpageId);

console.log(webpage);

Example response:

// WebpageEntity:
{
  id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
  slug: '/moved-here',
  name: 'Home Page',
  whenCreated: 1636212523899,
  whenPublished: 1636212525070,
  whenUpdated: 1636212525318,
  whenDraftSaved: 1636212524681,
  whenPreviewPublished: 1636212525030,
  redirectUrl: null,
  publishedUrl: 'https://amazing.pageblade.site/moved-here',
  previewUrl: 'https://amazing.pageblade.site/moved-here?eapY=preview',
  draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
  websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
  publishedVersions: [ 1636212525070 ],
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: getWebpages(paginatedRequest: PaginatedWebpageDto, tenantId?: string): Promise<PaginatedWebpageEntity>

Retrieves a page of webpages for the passed website default or passed tenant ID.

//
// Example: Get a single page of Webpages
//
const webpages = await pageBlade.getWebpages({
    websiteId,
    orderBy: 'whenCreated',
    orderDirection: ORDER_DIRECTION.ASC,
    pageIndex: 0,
    pageSize: 10,
});

console.log(webpages);

Example response:

{
  nextPageUrl: null,
  totalRowsCount: 1,
  rows: [
    {
      id: '23fteapYjMlgighjYO5oB8GfJ3nSrY4q',
      slug: '/moved-here',
      name: 'Home Page',
      whenCreated: 1636212523899,
      whenPublished: 1636212525070,
      whenUpdated: 1636212525318,
      whenDraftSaved: 1636212524681,
      whenPreviewPublished: 1636212525030,
      redirectUrl: null,
      publishedUrl: 'https://amazing.pageblade.site/moved-here',
      previewUrl: 'https://amazing.pageblade.site/moved-here?eapY=preview',
      draftUrl: 'https://amazing.pageblade.site/moved-here?eapY=draft',
      websiteId: 'wD9agU5FESFFRRah5AbITxQLnHgIxzbc',
      publishedVersions: [Array],
      tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
    }
  ],
  totalPagesCount: 1,
  pageIndex: 0,
  pageSize: 10,
  orderBy: 'whenCreated'
}

Method: getWebpageDraftHtml(webpageId: string, tenantId?: string): Promise<HtmlEntity>

Retrieves the HTML for the draft version of the page.

//
// Example: Get Webpage draft HTML
//
const html = await pageBlade.getWebpageDraftHtml(webpageId);

console.log(html);

Example response:

{ html: '<html><body>Draft html</body></html>' }

Method: getWebpagePublishedHtml(webpageId: string, tenantId?: string): Promise<HtmlEntity>

Retrieves the HTML for the published version of the page.

//
// Example: Get Webpage published HTML
//
const html = await pageBlade.getWebpagePublishedHtml(webpageId);

console.log(html);

Example response:

{ html: '<html><body>Home Updated</body></html>' }

Method: getWebpagePublishedVersionHtml(webpageId: string, version: number, tenantId?: string): Promise<HtmlEntity>

Retrieves the HTML for a previously published version of the page.

//
// Example: Get Webpage previously published version HTML
//
const html = await pageBlade.getWebpagePublishedVersionHtml(webpageId, version);

console.log(html);

Example response:

{ html: '<html><body>Previously Published html</body></html>' }

Assets

Assets storage is available for all your images, CSS and JS files and are allocated to a website for access on any of the associated webpages using a relative or full URL path.

Method: uploadAsset(websiteId: string, data: Readable | Buffer, filename: string, contentType: string, tenantId?: string): Promise<AssetEntity>

Uploads a new asset.

//
// Example: Upload asset using file stream
//
const stream = fs.createReadStream('image.png');
const asset = await pageBlade.uploadAsset(websiteId, stream, 'image.png', 'image/png');

console.log(asset);

Example response:

// AssetEntity:
{
  id: 'GHvvsIPbY37kCeopIwR3Jy5SOb6cFTtP',
  slug: '/assets/images/image.png',
  whenCreated: 1636214085343,
  whenUpdated: 1636214085343,
  bytes: 71065,
  websiteId: 'Oa6eqbuCdixoZbl7MJa6su3ptkIokrIr',
  filename: 'image.png',
  url: 'https://amazing.pageblade.site/assets/images/image.png',
  contentType: 'image/png',
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: getAsset(assetId: string, tenantId?: string): Promise<AssetEntity>

Retrieves details of an existing asset.

//
// Example: Get Asset
//
const asset = await pageBlade.getAsset(assetId);

console.log(asset);

Example response:

// AssetEntity:
{
  id: 'GHvvsIPbY37kCeopIwR3Jy5SOb6cFTtP',
  slug: '/assets/images/image.png',
  whenCreated: 1636214085343,
  whenUpdated: 1636214085343,
  bytes: 71065,
  websiteId: 'Oa6eqbuCdixoZbl7MJa6su3ptkIokrIr',
  filename: 'image.png',
  url: 'https://amazing.pageblade.site/assets/images/image.png',
  contentType: 'image/png',
  tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
}

Method: getAssets(paginatedRequest?: PaginatedAssetDto, tenantId?: string): Promise<PaginatedAssetEntity>

Retrieves a single page of assets.

//
// Example: Get a single page of Assets
//
const assets = await pageBlade.getAssets({
    websiteId,
    orderBy: 'whenCreated',
    orderDirection: ORDER_DIRECTION.ASC,
    pageIndex: 0,
    pageSize: 10,
});

console.log(assets);

Example response:

{
  nextPageUrl: null,
  totalRowsCount: 1,
  rows: [
    {
      id: 'GHvvsIPbY37kCeopIwR3Jy5SOb6cFTtP',
      slug: '/assets/images/image.png',
      whenCreated: 1636214085343,
      whenUpdated: 1636214085343,
      bytes: 71065,
      websiteId: 'Oa6eqbuCdixoZbl7MJa6su3ptkIokrIr',
      filename: 'image.png',
      url: 'https://amazing.pageblade.site/assets/images/image.png',
      contentType: 'image/png',
      tenantId: '9ZNyebUTK0O5dQicKHMCvrADaNlTXMGB'
    }
  ],
  totalPagesCount: 1,
  pageIndex: 0,
  pageSize: 10,
  orderBy: 'whenCreated'
}

Method: deleteAsset(assetId: string, tenantId?: string): Promise<void>

Deletes an asset by passing the asset ID.

//
// Example: Delete Asset
//
await pageBlade.deleteAsset(assetId);

Statistics

Retrieve statistics for either the passed tenant ID or for the entire account.

Method: getStatistics(tenantId?: string): Promise<StatisticsEntity>

Gets statistics for the account. Storage and bandwidth usage is defined in whole MB.

//
// Example: Get statistics for account (all tenants)
//
const statistics = await pageBlade.getStatistics();

console.log(statistics);

Example response:

// StatisticsEntity:
{
  bandwidth: { limitMb: 1000, usedMb: 0, remainingMb: 1000 },
  storage: { limitMb: 1000, usedMb: 0, remainingMb: 1000 },
  websites: { limit: 1000, used: 2, remaining: 998 },
  tenants: { limit: 1000, used: 1, remaining: 999 }
}

Gets statistics for the individual tenant ID.

//
// Example: Get statistics for a single tenant
//
const statistics = await pageBlade.getStatistics(tenantId);

console.log(statistics);

Example response:

// StatisticsEntity:
{
  bandwidth: { limitMb: 1000, usedMb: 0, remainingMb: 1000 },
  storage: { limitMb: 1000, usedMb: 0, remainingMb: 1000 },
  websites: { limit: 1000, used: 2, remaining: 998 },
  tenantId: '58IKD1P86CTkRyjsvBLWcWnFhkNLkGA9'
}

Tenants

All accounts have an associated default tenant. The tenant is the container for all websites, webpages, assets and tracking data.

Unless you are developing a multi-tenant application, it is unlikely you will need to specify the tenant ID, as the default tenant ID is used where unspecified.

Tenants allow you to provided completely isolated containers for your users. You can assign limits to restrict usage on bandwidth, storage and so on. It is important to provide the appropriate limiting values to your tenants so as not to exhaust your account.

Although it will accept any values, ultimately, they will not be allowed to exceed those defined in your account.

Most methods have an optional tenantId. On create methods, this will allocate the object to the tenant and on read methods, it will only return objects that belong to the tenant.

Note storage and bandwidth limits are defined in whole MB.

Method: createTenant(tenant: TenantCreateDto): Promise<TenantEntity>

Creates a new tenant using the DTO.

//
// Example: Create Tenant
//
const tenant = await pageBlade.createTenant({
    name: 'Mega Store',
    bandwidthLimit: 10000000,
    storageLimit: 100000000,
    websitesLimit: 10,
    customDomains: false,
});

console.log(tenant);

Example response:

{
  id: 'dYQEbRWekbbFOcTnRx45iDkfQYbhYSN6',
  name: 'Mega Store',
  bandwidthLimitMb: 10000000,
  storageLimitMb: 100000000,
  websitesLimit: 10,
  whenCreated: 1636294825679,
  whenUpdated: 1636294825679,
  customDomains: false,
  websiteAssetsLimit: 1000,
  websitePagesLimit: 1000
}

Method: updateTenant(tenant: TenantUpdateDto, tenantId?: string): Promise<WebpageEntity>

Updates an existing tenant using the DTO.

//
// Example: Update Tenant to limit the number of websites allowed to 1
//
const tenant = await pageBlade.updateTenant(
    {
        websitesLimit: 1,
    },
    tenantId,
);

console.log(tenant);

Example response:

{
  id: 'dYQEbRWekbbFOcTnRx45iDkfQYbhYSN6',
  name: 'Mega Store',
  bandwidthLimitMb: 10000000,
  storageLimitMb: 100000000,
  websitesLimit: 1,
  whenCreated: 1636294825679,
  whenUpdated: 1636294825715,
  customDomains: false,
  websiteAssetsLimit: 1000,
  websitePagesLimit: 1000
}

Method: getTenant(tenantId: string): Promise<TenantEntity>

Retrieves tenant details.

//
// Example: Get Tenant
//
const tenant = await pageBlade.getTenant(tenantId);

console.log(tenant);

Example response:

{
  id: 'dYQEbRWekbbFOcTnRx45iDkfQYbhYSN6',
  name: 'Mega Store',
  bandwidthLimitMb: 10000000,
  storageLimitMb: 100000000,
  websitesLimit: 1,
  whenCreated: 1636294825679,
  whenUpdated: 1636294825715,
  customDomains: false,
  websiteAssetsLimit: 1000,
  websitePagesLimit: 1000
}

Method: getTenants(paginatedRequest?: PaginatedTenantDto): Promise<PaginatedTenantEntity>

Retrieves a single page of tenants.

//
// Example: Get a single page of Tenants
//
const tenants = await pageBlade.getTenants({
    orderBy: 'whenCreated',
    orderDirection: ORDER_DIRECTION.ASC,
    pageIndex: 0,
    pageSize: 3,
});

console.log(tenants);

Example response:

{
  nextPageUrl: null,
  totalRowsCount: 2,
  rows: [
  {
      id: '58IKD1P86CTkRyjsvBLWcWnFhkNLkGA9',
      name: 'Default',
      bandwidthLimitMb: 1000,
      storageLimitMb: 1000,
      websitesLimit: 1000,
      whenCreated: 1636280236583,
      whenUpdated: 1636280236583,
      customDomains: true,
      websiteAssetsLimit: 1000,
      websitePagesLimit: 1000
    },
    {
      id: 'dYQEbRWekbbFOcTnRx45iDkfQYbhYSN6',
      name: 'Mega Store',
      bandwidthLimitMb: 10000000,
      storageLimitMb: 100000000,
      websitesLimit: 1,
      whenCreated: 1636294825679,
      whenUpdated: 1636294825715,
      customDomains: false,
      websiteAssetsLimit: 1000,
      websitePagesLimit: 1000
    }
  ],
  totalPagesCount: 1,
  pageIndex: 0,
  pageSize: 3,
  orderBy: 'whenCreated'
}

Method: deleteTenant(tenantId: string): Promise<void>

Deletes a single tenant for the passed ID.

//
// Example: Delete Tenant
//
await pageBlade.deleteTenant(tenantId);

Pagination

Tenants, Websites, Webpages and Assets all implement common request and response attributes for pagination, all attributes being optional.

pageIndex starts from zero for the first page of results.

pageSize can accept a value between 1 and 100.

keyword allows you to filter results based on a keyword match in the named entity.

orderBy value is dependent on the entity type, though always includes whenCreated and whenUpdated

orderDirection can be either ORDER_DIRECTION.ASC or ORDER_DIRECTION.DESC

// Example: PaginatedWebpageDto
{
    pageIndex?: number;
    pageSize?: number;
    keyword?: string;
    orderBy?: 'whenCreated' | 'whenUpdated' | 'whenPublished' | 'slug' | 'redirectUrl';
    orderDirection?: ORDER_DIRECTION;
};

The common response format:

export type PaginatedBaseEntity<T> = {
    nextPageUrl: string;
    orderBy: string;
    pageIndex: number;
    pageSize: number;
    rows: T[];
    totalPagesCount: number;
    totalRowsCount: number;
};

Rate Limiting

The PageBlade API implements request rate limiting. This SDK uses Got as the http client and is configured to respect the retry-after header response, retrying the request after the elapsed period.

It is therefore recommended to execute methods sequentially to reduce the chance of a request failing completely.

The rate limiting configuration on the PageBlade server may vary, though the actual limits are exposed in the response headers as X-Rate-Limit-Limit, X-Retry-Remaining and X-Retry-Reset.


Timestamps

All timestamps, whenUpdated, whenCreated and others are in Unix Timestamp milliseconds format (UTC).


Error Handling

It is recommended to wrap all requests in a try {} catch () {} The SDK will present the error code and message as sent from the API call, for example:

// Attempt to retrieve website
try {
    await pageBlade.getWebsite('invalid-id');
} catch (error) {
    console.log(error);
}

// Response:
Error: website not found
code: 404

Bandwidth

The hosting engine tracks outbound bandwidth use over a rolling 30 days. The current usage can be checked using the statistics methods. If a request for a page or asset takes the total usage over the limits specified in either the tenant or user account, the visitor will receive a 509 Bandwidth Exceeded error.


Roadmap

November 2021

  • SDK available
  • Public access for free accounts

December 2021

  • Development and Production ready accounts activated:
    • Custom domains
    • Visitor tracking analytics

January 2022

  • Enterprise accounts activated:
    • Multi-tenant accounts
    • White-labeled subdomain hosting

Suggestions & Discussions

Suggestions to improve PageBlade and to discuss current features are welcome! Start a discussion.


SDK Contributions

Contributions, enhancements, and bug-fixes are welcome! Open an issue on GitHub and submit a pull request.


Building

To build the project locally on your computer:

  1. Clone this repo git clone https://github.com/scoritz/pageblade-sdk.git

  2. Install dependencies npm install

  3. Build the code npm run build


License

pageblade-sdk is open-source, under the MIT license.