Discovery Days at Medusa: How I created Cross Post tool from Notion
Tags: opensource, javascript, showdev, productivity
Published: 2023-02-16 13:44:01


I’m a technical writer at [Medusa](https://medusajs.com/). Part of my work includes managing the content on our blog and across platforms. Typically, we publish an article on our [blog](https://medusajs.com/blog/) (which is built with [Stackbit](https://www.stackbit.com/)), then cross-post that article across platforms such as Dev.to, Hashnode, and Medium.


This process has become tedious and time-consuming. We publish at least 2 articles per week, and each article can take more than 30 minutes to be published across our blog and the 3 mentioned platforms. This includes adding the content in the format that each platform supports, uploading images if there are any, setting meta attributes such as description and meta tags, and more.


So, I decided to build a cross posting tool that would automate this task. [cross-post-notion](https://github.com/shahednasser/cross-post-notion) now allows us to publish an article from Notion to a GitHub repository (which holds the content of our main blog), Dev.to, Hashnode, and Medium in less than a minute. I decided to build this tool as part of our Discovery Days at Medusa.


## What are Discovery Days?


Every Friday, the Medusa team members can dedicate half or their full day towards doing something different from their typical daily work. You can use this day to learn something new, read a book specific to your field, try out an interesting tool, or build a new tool.


As I realized this tool would heavily reduce the time I spend on publishing content to our blog, I dedicated the past 4 Discovery Days (Fridays) to building this tool.


## How it Works


You can use `cross-post-notion` either as an NPX command, as a global CLI tool, or just by cloning the [GitHub repository](https://github.com/shahednasser/cross-post-notion#notion).


Then, after adding the configurations necessary (more on this later), you can publish to all the different platforms using one command. You can also choose specific platforms to publish to.


### Publishing to GitHub


Publishing to GitHub can be useful for any blogging platform that uses Markdown files hosted in a GitHub repository. This includes Stackbit, GitHub Pages, 11ty, Hugo, Docusaurus, etc…


Articles are pushed to GitHub as Markdown files with front matter metadata. The front matter includes by default the `title` and `date` of the article. The values of these fields are extracted from your Notion document, and you can specify the properties to extract these fields from.


In addition, you can specify custom front matter fields to add and from which properties to extract them from Notion. You can also specify front matter fields with fixed values. This is helpful if you have front matter fields that you need to always add to your articles.


If an article has images, the images are pushed to the GitHub repository as well. You can specify the path to upload the images to and the prefix to use within the article.


### Publishing to Dev.to


You can publish articles to dev.to with this tool, either on your personal account, or in an organization. Similar to posting to GitHub, the values of dev.to fields such as the title, tags, or series can all be extracted from Notion properties.


One limitation to publishing to dev.to is that you can’t upload the images in the article using their APIs. So, you’ll still have to manually go in and upload any images in your article.


### Publishing to Hashnode


You can publish articles to Hashnode with this tool, either on your personal blog or in a specific publication. Similar to the other platforms, the values of Hashnode fields are extracted from Notion properties.


One limitation to publishing to Hashnode is that it’s not possible to publish an article as a draft with their APIs. It’s only possible to choose to hide it from Hashnode’s feed. So, the article will automatically be available to your followers.


### Publishing to Medium


You can publish articles to Medium with this tool, either on your personal account or in a specific publication. Similar to the other platforms, the values of Medium fields are extracted from Notion properties.


## Using this Tool


### Requirements


This tool requires an Internal Notion Integration created and installed in your Notion workspace. The integration must have access to the pages you want to publish as well. You can learn more about this in [Notion’s documentation](https://developers.notion.com/docs/create-a-notion-integration#step-1-create-an-integration).


### Install the Tool


There are three ways you can use this tool:

1\. With NPX, which does not directly install the tool on your machine:

```bash
npx cross-post-notion
```

2\. Install as a global CLI tool with NPM or Yarn:

```bash
# using NPM
npm install -g cross-post-notion

# using Yarn
yarn add cross-post-notion global
```

3\. Clone the GitHub repository:

```bash
git clone https://github.com/shahednasser/cross-post-notion.git
cd cross-post-notion
npm install
npm start
```


### Set Environment Variables


You need to set the following environment variables before you can use the tool. The requirements of the environment variables depends on which platforms you want to publish to:

- Notion:
	- `NOTION_TOKEN`: (required for all platforms) Token on a Notion internal integration
- GitHub:
	- `GH_TOKEN`: (required) GitHub personal token
	- `GH_OWNER`: (required) GitHub username
	- `GH_REPO`: (required) GitHub Repository name
	- `GH_BRANCH`: Branch name. Default is `master`.
- Dev.to:
	- `DEVTO_API_KEY`: (required) Your personal dev.to API key. Learn how to retrieve it [here](https://developers.forem.com/api/v0).
	- `DEVTO_ORG_ID`: The ID of the organization to publish the article under. You can retrieve it either from the organization dashboard page, where the ID is the last part of the URL (`https://dev.to/dashboard/organization/ORG_ID`). Alternatively, you can use Dev.to's [List Organizations](https://developers.forem.com/api/v0#tag/organizations/operation/getOrgUsers) endpoint to find the ID.
- Hashnode:
	- `HASHNODE_TOKEN`: (required) Hashnode personal token
	- `HASHNODE_PUB_ID`: The ID of the publication to publish the article under. You can retrieve it either from the publication's dashboard page, where the ID is the second part of the URL (`https://hashnode.com/PUB_ID/dashboard`).
- Medium:
	- `MEDIUM_TOKEN`: (required) Medium Integration Token. Can be retrieved from [here](https://medium.com/me/settings/security).
	- `MEDIUM_PUB_NAME`: The name of the Medium publication. Must be the exact name as it is on Medium.

### Add Configurations


Configurations are used to customize how the article is published on the different platforms. They can be used to define what properties to extract data from in Notion, whether the article should be published or added as a draft, and more. You only need to set these configurations the first time you use the tool.


By default, configurations are loaded from the file `config/default.json` relative to the current directory you’re running the command from. Alternatively, you can pass the [path to the config file as an option](https://github.com/shahednasser/cross-post-notion#config).


The file can have the following configurations:


```bash
{
  "config": {
    "notion": {
      "options": {
        "skip_block_types": [
	        "toggle"
        ]
      }
    },
    "github": {
      "options": {
        "image_path": "public",
        "image_prefix": "/",
        "article_path": "content",
        "properties": {
          "title": "Title for Blog",
          "date": "Publishing Date",
          "slug": "Slug"
        },
        "add_default_frontmatter": true,
        "frontmatter_labels": {
          "title": "title",
          "date": "date"
        },
        "extra_frontmatter": {
          "excerpt": "this is description"
        },
        "extra_frontmatter_mapper": {
          "excerpt": "Description"
        }
      }
    },
    "devto": {
      "options": {
        "should_publish": false,
        "properties": {
          "title": "Title for Dev.to"
        }
      }
    },
    "hashnode": {
      "options": {
        "should_publish": true,
        "should_notify_followers": false,
        "properties": {
          "title": "Title for Hashnode"
        }
      }
    },
    "medium": {
      "options": {
        "should_publish": false,
        "should_notify_followers": false,
        "properties": {
          "title": "Title for Medium"
        }
      }
    }
  }
}
```


You can learn what each of these fields are and what more fields can be accepted in [the GitHub repository](https://github.com/shahednasser/cross-post-notion#available-configurations).


### Run Post Command


Grab the URL of a Notion document that includes an article you want to publish. It should be an internal URL which you can obtain from the Notion document by:

- Click on Share at the top right.
- Click on “Copy link” at the bottom of the pop-up.

 Then, run the following command:


```bash
# using NPX
npx cross-post-notion post <url>

# using CLI tool
cross-post-notion post <url>

# using cloned repository
npm start post <url>
```


Where `<url>` is the URL to the Notion document.


If all environment variables are set correctly, you should see logged in your terminal that the article was published on each of the platforms.


You can also specify which platforms you want to publish the article on using the `-p` option:


```bash
npx cross-post-notion post <url> -p devto hashnode
```


This would publish the article only on [Dev.to](http://Dev.to) and Hashnode.


## Learn More about this Tool


You can learn more about this tool and its limitations by checking out the [GitHub repository](https://github.com/shahednasser/cross-post-notion). If you use it, let me know if it helps out! Although I’m mainly focused on using it for publishing articles at Medusa, feel free to open issues and contribute to further improve the tool.


> Be sure to check out [Medusa, a composable ecommerce platform, on GitHub](https://github.com/medusajs/medusa).