Skip to content

Latest commit

 

History

History

astro

Polar Astro SDK

The Polar Astro SDK is an Astro library designed for interacting with the Polar API.

The SDK is designed to run in both static and SSR mode, and provides wrappers the @polar-sh/sdk package for integrating your Astro site with Polar.

See astro-example for a fully working example of how to integrate @polar-sh/astro with your Astro app.

Usage

Install the SDK with your favourite package manager (ours is PNPM!):

pnpm add @polar-sh/astro

Then, get started by creating a Polar client with your access token:

import { Polar } from '@polar-sh/astro';

const polar = new Polar({ accessToken });

Access tokens can be generated on your Polar Settings page.

Custom frontmatter schema

The Polar Astro SDK ships with a Zod schema for defining frontmatter properties in your Astro content collections. It contains common Polar article metadata, such as a title and whether or not to notifySubscribers of the article.

import { z, defineCollection } from 'astro:content';
import { polarArticleSchema } from '@polar-sh/astro';

defineCollection({
  schema: z.object({
    // Your custom frontmatter properties here...
    // ...
    polar: polarArticleSchema.optional(),
  }),
});

Uploading posts to Polar

The upload module is designed to be used with Astro content collections.

The upload client uses the builder pattern to transform and filter posts before uploading them to Polar. For example, to upload all your posts with no modification:

import { getCollection } from 'astro:content';
import { Polar } from '@polar-sh/astro';

const posts = await getCollection('blog');

const polar = new Polar({ accessToken });

const { data, error } = await polar.upload(posts, {
  organizationId,
});

The above code will upload all your posts to Polar, and will update any existing posts that share a slug between Astro and Polar. The default parameters are as follows:

{
  title: entry.id,
  slug: entry.slug,
  body: entry.body,
}

Where entry is an Astro collection entry object. entry.id usually corresponds to the filename.

Both organizationId and organizationName are required fields. The returned data will contain two arrays: one for newly-created posts, and one for updated posts.

The error property of the response will contain either an error or a group of errors.

data and error are mutually exclusive.

The Polar upload builder includes functions for transforming and filtering your posts before they're uploaded.

Examples

Filter existing posts

Only upload new posts that haven't previously been uploaded to Polar. Posts are deduplicated by slug.

const { data, error } = await polar
  .upload(posts, {
    organizationId,
  })
  .filter(({ exists }) => !exists);
Add a title to the posts

This assumes you have a title in your content collection frontmatter.

const { data, error } = await polar
  .upload(posts, {
    organizationId,
  })
  .transform(
    ({
      // `entry` is the Astro collection entry
      entry,
      // `article` is the Polar article
      article,
      // `existing` is the existing Polar article, if it's been uploaded previously
      existing,
    }) => {
      article.title = entry.data.title ?? existing.title;
      return article;
    }
  );
Update existing posts with new hero images

If you have hero images on your Astro collection entries, you can add them as markdown images.

const { data, error } = await polar
  .upload(posts, {
    organizationId,
  })
  .filter(({ exists }) => exists)
  .transform(({ entry, article }) => {
    if (entry.data.image) {
      // Add the image as a markdown image at the start of the article
      article.body = `![](${Astro.url.host}${entry.data.image.src})\n\n${article.body}`;
    }
    return article;
  });
Using the custom frontmatter schema

You can use the custom frontmatter schema directly with the upload client. This article assumes the properties exist under a polar key in your frontmatter.

const { data, error } = await polar
  .upload(posts, {
    organizationId,
  })
  .filter(({ exists }) => exists)
  .transform(({ entry, article }) => {
    article = {
      ...article,
      ...entry.data.polar,
      // `published_at` is a date and needs to be a string
      published_at: entry.data.polar?.published_at?.toISOString(),
    };
    return article;
  });

Using the Polar SDK

You can also access other Polar SDK functions through the client property on the Polar class.

pledge = await polar.client.pledges.get({ id: 'pledge-id' });

TODOs

  • Add Polar article upload
  • Add functions for authenticating on an Astro site using Polar
    • Add helper methods for determining a user's subscription tier
  • Add Polar-specific components like <Paywall>
    • These will probably be React components, as Polar is written in React and Astro supports React
  • Add functionality for pulling articles from Polar to generate static or SSR pages on Polar sites