(Partially working)🙃 Automate Blog Post Publishing from Dev.to to Hashnode using Express Server(Importer) - #APIHackathon
Introduction
Embarking on the challenging terrain of the Hashnode Hackathon, this project unfolds as a testament to the relentless pursuit of web development excellence. While the completion of the project remains an aspiration at this juncture, the journey has been a profound exploration of technologies, problem-solving, and skill
Framework Architecture with Express
The project's architecture relies on Express, a robust Node.js web application framework. Configured meticulously, Express seamlessly handles HTTP requests, incorporates body parsing, and efficiently serves static files. The portability and efficiency of Express lay a solid foundation for the entire project.
import express from "express";
import bodyParser from "body-parser";
import { getDevToArticleData } from './fetchApiData.js';
import { publishArticleToGraphQL } from './postApiData.js';
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
const port = 3000;
// Root endpoint, Once this endpoint is hit, The index.ejs file will be renderded in the client side
app.get("/", async (req, res) => {
res.render("index.ejs")
});
app.post('/submit', async (req, res) => {
try {
const url = req.body.data; // Extractiong URL from the client side request
// Use a regular expression to extract username and slug
const match = url.match(/https:\/\/dev\.to\/([^\/]+)\/([^\/]+)/);
if (match && match.length === 3) {
const username = match[1];
const slug = match[2];
console.log('Username:', username);
console.log('Slug:', slug);
const articleData = await getDevToArticleData(username, slug);
console.log(articleData);
const publishedArticle = await publishArticleToGraphQL(articleData);
// Assuming you want to send some response back
res.send(`Article published successfully for ${username}/${slug}`);
} else {
console.log('Invalid URL format');
res.status(400).send('Invalid URL format');
}
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);;
});
Unveiling DevTo Article Data
The getDevToArticleData
function serves as our passport to the treasure trove of DevTo articles. By providing a username and a unique article slug, this function seamlessly interacts with the DevTo API, fetching comprehensive data about a specific article. The response from the DevTo API is dissected to create a structured data object. Each property within this object encapsulates essential information about the article, including its ID, title, author details, URLs, cover images, and the article's content in both markdown and HTML formats.
async function getDevToArticleData(username, slug) {
try {
const response = await axios.get(`https://dev.to/api/articles/${username}/${slug}`, {
headers: {
"Accept": acceptHeader,
"api-key": DevToApiKey,
},
});
// Processing the response
if (response.status === 200) {
const responseData = response.data;
// Extracting relevant data
const dataObjectPropertyFromDevTo = {
id: responseData.id,
slug: responseData.slug,
title: responseData.title,
author: {
id: responseData.user.user_id,
username: responseData.user.username,
name: responseData.user.name,
profilePicture: responseData.user.profile_image,
},
url: responseData.url,
canonicalUrl: responseData.canonical_url,
coverImage: {
url: responseData.cover_image,
},
content: {
markdown: responseData.body_markdown,
html: responseData.body_html,
},
};
// Returning the curated data
return dataObjectPropertyFromDevTo;
} else {
// Throwing an error if the response status is not 200
throw new Error(`Error fetching data. Status code: ${response.status}`);
}
} catch (error) {
// Handling errors during the API request
throw new Error(`Error making request: ${error.message}`);
}
}
// Exporting the function for use in our project
export { getDevToArticleData };
Crafting the GraphQL Client for Hashnode
The publishArticleToGraphQL function leverages the Axios library to create a GraphQL client tailored for Hashnode's API. By setting the base URL to 'https://gql.hashnode.com/' and configuring the necessary headers, we establish a secure and efficient channel for interacting with Hashnode's GraphQL endpoint.
import axios from 'axios';
const hashNodeApiKey = ""; // Replace with your actual API key
const graphqlClient = axios.create({
baseURL: 'https://gql.hashnode.com/',
headers: {
'Content-Type': 'application/graphql',
'Authorization': hashNodeApiKey,
},
});
const publishArticleMutation = `
mutation PublishPost($input: PublishPostInput!) {
publishPost(input: $input) {
post {
id
slug
title
author {
id
username
name
profilePicture
}
url
canonicalUrl
coverImage {
url
}
content {
markdown
html
}
}
}
}
`;
async function publishArticleToGraphQL(dataHeadersFromDevTo) {
try {
console.log("Posting data to GraphQL...");
const response = await graphqlClient.post('', {
query: publishArticleMutation,
variables: {
input: dataHeadersFromDevTo,
},
});
if (response.status === 200) {
const publishedArticle = response.data?.data?.publishPost?.post;
if (publishedArticle) {
console.log("Article published successfully!");
return publishedArticle;
} else {
throw new Error("Error: Invalid response structure. Post data not found.");
}
} else {
throw new Error(`Error publishing article. Status code: ${response.status}`);
}
} catch (error) {
console.error(`Error making GraphQL request: ${error}`);
throw error;
}
}
export { publishArticleToGraphQL };
Check out the video
Conclusion
Although, I was not able to finish the project , I really feel good for my dedication towards a purpose and it was my first try to build in hackathons. I'm very open to fixes and if time permits help me completing the project. Special thanks for the stage and opportunity.
Subscribe to my newsletter
Read articles from Shibu Joseph directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by