Cross-post from your blog to DEV.to (Node.js script)

Reading time: 4 min

Last updated

_,.-'~'-.,__,.-'~'-.,__,.-'~'-.,__,.-'~'-.,__,.-'~'-.,_

DEV.to is a great place for a technical blog. The website feels lightweight and easy to navigate and the community is welcoming.
Still, you may want to publish your content under your own domain first which you have full control of. You then may want to cross-post to DEV with a link to the canonical URL.

When I started writing initially my workflow was like this:

  • Write a blog post in the comfort of my text editor locally
  • Publish to my personal blog
  • Cross-post to DEV manually

Last part definitely called for automation. There's a way to Publish from RSS but I realized some tweaks had to be made to the content before it could be published on DEV.

Another approach is to use the DEV API. My blog is built with Eleventy and I've added a little helper npm script to help with cross-posting. It loads a blog post from the markdown file, publishes a draft at DEV and returns a URL of the draft. There you can make sure it looks alright (occasionally I may need to adjust tags) and hit Publish.

Workflow now looks like this:

  • Write a blog post in the comfort of my text editor locally
  • Publish to my personal blog
  • Run npm run <path-to-md-file> → follow the draft link to review → Publish

Cross-post to DEV.to with Node.js script

If it sounds useful, below is a (beginner-friendly) guide of how to add such script to your own blog.

Create a DEV API key #

In your DEV profile, go to SettingsAccountDEV Community API Keys

Give the key a name (e.g. publish-to-dev) and generate it.

Copy the key and put it in the .env file in the root of your blog. Make sure this file is listed in .gitignore as we don't want secrets to land in a git repository.

.env

DEV_TO_API_KEY=your-api-key

npm script to publish to DEV #

If not installed, you will need to add these packages to dev dependencies:

npm i --save-dev dotenv gray-matter node-fetch

You can find the entire script here: https://github.com/maximivanov/maxivanov.io/blob/main/scripts/publish-to-devto.js

To start, we load the .env file, include dependencies and configure some settings.

./scripts/publish-to-devto.js

require('dotenv').config() // make the API key available as an environment variable

const matter = require('gray-matter') // library to parse front-matter and content from posts' markdown files
const fetch = require('node-fetch')

const apiKey = process.env.DEV_TO_API_KEY
const apiUrl = 'https://dev.to/api/articles'
const siteUrl = 'https://www.maxivanov.io' // hostname of the blog
const autoPublish = false // whether we want to publish or create drafts

Main body of the script, which fetches the data, sends the API request and prints the draft URL:

./scripts/publish-to-devto.js

...

;(async () => {
const path = process.argv.slice(2)[0] // read file name from command line arguments
const file = matter.read(path)

const payload = getPayload(file) // get payload to publish to DEV API (see below)
const article = await publish(payload)

if (autoPublish) {
console.log(`Article published: ${article.url}`)
} else {
console.log(`Article draft created: ${article.url}/edit`)
}
})()

Function to prepare data to post to the API:

./scripts/publish-to-devto.js

...

function getPayload(file) {
return {
article: {
title: file.data.title.trim(),
// replace relative paths with absolute URLs
body_markdown: file.content.trim().replace(/\]\(\/posts\//gm, `](${siteUrl}/posts/`),
published: autoPublish,
// if you want blog post to be a part of Series on DEV
series: undefined,
// cover image URL
main_image: file.data.image ? `${siteUrl}${file.data.image}` : undefined,
// generate the canonical url (file name minus .md in my case)
canonical_url: `${siteUrl}/${file.path.split('/').slice(-2, -1)[0]}`,
description: file.data.description,
// DEV allows only 4 tags and they must be alphanumeric
tags: file.data.tags.slice(0, 4).map(tag => tag.toLowerCase().replace(/[^a-z0-9]/i, '')),
}
}
}

And finally a function to publish the prepared payload to the API:

./scripts/publish-to-devto.js

...

async function publish(payload) {
const response = await fetch(apiUrl, {
method: 'post',
body: JSON.stringify(payload),
headers: {'Content-Type': 'application/json', 'api-key': apiKey}
});

const json = await response.json();
if (json.error) {
throw new Error(`API returned an error: ${json.error}`)
}

return json
}

Add a new script to the package.json:

./package.json

{
...
"scripts": {
...
"publish-to-devto": "node ./scripts/publish-to-devto.js"
}
}

And call it from the command line:

npm run publish-to-devto posts/2021/01/add-docker-container-name-to-shell-prompt.md

... #

Alright! We just got rid of some boring manual work which is always good.

If you like this type of content you can follow me on Twitter for the latest updates.

_,.-'~'-.,__,.-'~'-.,__,.-'~'-.,__,.-'~'-.,__,.-'~'-.,_

Next: Domain and path redirects with Cloudflare

Previous: Shell alias to view diffs in Github