Are you a blog writer? Hate cross-posting your content across the web? You're in luck!
This solution will hook into your blog creation process and automatically cross-post your content for you to Medium, Dev.to, and Hashnode!
Deploy into your AWS account and type away!
For a full summary of this solution please refer to this blog post by Allen Helton.
For cross-posts to work successfully, there are a few prereqs that must be met in your setup.
- Your blog post must be written in markdown.
- Content is checked into a repository in GitHub
- You have an application in AWS Amplify that has a runnable CI pipeline
- Blog posts have front matter in the format outlined in the Blog Metadata section
Note - it is highly recommended you host your blog on your own site. This guarantees you own your content and prevents accidental loss if your favorite platform goes down or has an incident. It also enables easy canonification of your content when it is cross posted so it ranks higher in search engine results. For a step by step guide on hosting your own blog for free, please reference this post.
The cross posting process is outlined below.
- Completed blog post written in markdown is committed to main branch
- AWS Amplify CI pipeline picks up changes and runs build
- On success, Amplify publishes a
Amplify Deployment Status Change
event to EventBridge, triggering a Lambda function deployed in this stack - The function uses your GitHub PAT to identify and load the blog post content and pass it into a Step Function workflow
- The workflow will do an idempotency check, and if it's ok to continue will transform and publish to Medium, Hashnode, and Dev.to in parallel
- After publish is complete, the workflow checks if there were any failures.
- If there was a failure, it sends an email with a link to the execution for debugging
- On success, it sends an email with links to the published content and updates the idempotency record and article catalog
Note - If you do not provide a SendGrid API key, you will not receive email status updates
This solution will take content you create and automatically cross-post it on three platforms:
You are required to have an account on all three platforms and must generate an API key for each of them.
Optionally, you can publish straight to publications on each of the platforms. If there is a specific organization on Dev.to or publication on Medium or Hashnode you typically write for, you can fill out DevOrganizationId
, MediumPublicationId
, and HashnodePublicationId
deployment variables respectively. For example, you could automatically submit your story to the AWS Community Builders organization on dev.to instead of under your name.
The solution is built using AWS SAM. To deploy the resources into the cloud you must install the SAM CLI.
Once installed, run the following commands in the root folder of the solution.
sam build --parallel
sam deploy --guided
This will walk you through deployment, prompting you for all the parameters necessary for proper use. Below are the parameters you must fill out on deploy.
Parameter | Description | Required |
---|---|---|
TableName | Name of the DynamoDB table to create | No |
GSI1 | Name of the GSI on the DDB table | No |
GitHubPAT | Personal Access Token to load newsletter content from your repository | Yes |
GitHubOwner | The GitHub user name that owns the repository for your content | Yes |
GitHubRepo | The repository name that contains your content | Yes |
AmplifyProjectId | Identifier of the Amplify project that builds your newsletter | Yes |
MediumApiKey | API key used to manipulate data in your Medium account | Yes |
MediumPublicationId | Identifier of the publication you wish to submit to on Medium | No |
MediumAuthorId | Identifier of your user on Medium | Yes if MediumPublicationId is not provided |
DevApiKey | API key used to manipulate data in your Dev.to account | Yes |
DevOrganizationId | Identifier of the organization you wish to submit to on Dev.to | No |
HashnodeApiKey | API key used to manipulate data in your Hashnode account | Yes |
HashnodePublicationId | Identifier for your blog publication on Hashnode | Yes |
HashnodeBlogUrl | Base url of your blog hosted in Hashnode | Yes |
BlogBaseUrl | Vase url of your blog on your personal site | Yes |
BlogContentPath | Relative path from the root directory to the blog content folder in your GitHub repo | Yes |
SendgridApiKey | Api Key of the SendGrid account that will send the status report when cross-posting is complete | No |
NotificationEmail | Email address to notify when cross posting is complete | No |
SendgridFromEmail | Email address for SendGrid that sends you the status email | No |
If you wish to get notification emails on the status of the cross posting, you must use SendGrid. SendGrid offers a generous free tier for email messages and is quick to get started. To configure SendGrid to send you emails you must:
Once you perform the above actions, you may use the values in the respective deployment variables listed above.
In the event the cross-posting does not work, it can be safely retried without worrying about pushing your content multiple times. Each post will update the idempotency DynamoDB record for the cross-posting state machine. This record holds the status (success/failure) for each platform. If the article was successfully posted on a platform, it will be skipped on subsequent executions.
Your blog must be written in Markdown for this solution to work appropriately. To save metadata about your post, you can add front matter at the beginning of the file. This solution requires a specific set of metadata in order to function appropriately.
Example
---
title: My first blog!
description: This is the subtitle that is used for SEO and visible in Medium and Hashnode posts.
image: https://link-to-hero-image.png
image_attribution: Any attribution required for hero image
categories:
- categoryOne
tags:
- serverless
- other tag
slug: /my-first-blog
---
Field | Description | Required? |
---|---|---|
title | Title of the blog issue | Yes |
description | Brief summary of article. This shows up on Hashnode and Medium and is used in SEO previews | Yes |
image | Link to the hero image for your article | Yes |
image_attribution | Any attribution text needed for your hero image | No |
categories | Array of categories. This will be used as tags for Dev and Medium | No |
tags | Array of tags. Also used as tags for Dev and Medium | No |
slug | Relative url of your post. Used in the article catalog | Yes |
One of the neat features provided by this solution is substituting relative urls for the appropriate urls on a given page. For example, if you use a relative url to link to another blog post you've written on your site, this solution will replace that with the cross-posted version. So Medium articles will always point to Medium articles, Hashnode articles will always point to Hashnode, etc...
This is managed for you by the solution. It creates entries for your content in DynamoDB with the following format:
{
"pk": "<article slug>",
"sk": "article",
"GSI1PK": "article",
"GSI1SK": "<title of the post>",
"links": {
"url": "<article slug>",
"devUrl": "<full path to article on dev.to>",
"mediumUrl": "<full path to article on Medium>",
"hashnodeUrl": "<full path to article on Hashnode>"
}
}
When transforming your Markdown content, it will load all articles from DynamoDB, use a Regex to match on the article slug in your content, and replace with the url of appropriate site.
If you already have a number of articles and wish to seed the database with the cross references, you will have to compile the data manually and put it in the following format:
[
{
"title": "<title of article>",
"devUrl": "<url of article on dev.to>",
"url": "<relative url of article on your blog>",
"mediumUrl": "<url of article on medium>",
"hashnodeUrl": "<url of article on hashnode>"
}
]
Take this data and update the load-cross-posts function to load and handle that data. Run the function manually to seed the data in your database table.
If you are embedding content in your posts, they might not work out of the box. There is only support for Hugo twitter embeds. The format of a Hugo Twitter embed is:
{{<tweet user="" id="">}}
If you include this in your content, it will be automatically transformed to the appropriate embed style on the appropriate platform.
Below are a list of known limitations:
- Your content must be written in Markdown with front matter describing the blog post.
- Content must be hosted in GitHub.
- You are required to post to Dev.to, Medium, and Hashnode. You cannot pick and choose which platforms you want to use.
- Only Hugo style Twitter embeds are supported. Embeds for other content will not work.
- This process is triggered on a successful build of an AWS Amplify project. Other triggers are not supported (but can easily be modified to add them).
- Notifications are limited to sending emails in SendGrid.
- The only way to deploy the solution is with AWS SAM.
Please feel free to contribute to this project! Bonus points if you can meaningfully address any of the limitations listed above :)
This is an AWS Community Builders project and is meant to help the community. If you see fit, please donate some time into making it better!