DEV Community

shrey vijayvargiya
shrey vijayvargiya

Posted on

POST API for pushing content on Devto

First of all let's see the code below

// Function to post to Dev.to
app.post("/post-to-devto", async (c) => {
    try {
        const { title } = await c.req.json();
        if (!title) {
            c.status(400);
            return c.json({
                error: "Title is required",
            });
        }

        // Check if DEV_TO_API token is available
        if (!process.env.DEV_TO_API_TOKEN) {
            c.status(500);
            return c.json({
                error: "DEV_TO_API_TOKEN environment variable is not set",
            });
        }

        // Fetch the document from Firestore publish collection using the title
        const postsSnapshot = await firestore
            .collection("publish")
            .where("title", "==", title.replaceAll("-", " "))
            .get();

        if (postsSnapshot.empty) {
            c.status(404);
            return c.json({
                error: `No post found with title: ${title}`,
            });
        }

        const postDoc = postsSnapshot.docs[0];
        const postData = postDoc.data();

        // Validate required fields
        if (!postData.content && !postData.htmlContent) {
            return c.json(
                {
                    success: false,
                    error: "Post content is required (content or htmlContent field)",
                },
                400
            );
        }

        // Prepare the article data for Dev.to API
        const processedTags = (() => {
            // Handle tags properly for Dev.to API
            if (!Array.isArray(postData.tags) || postData.tags.length === 0) {
                return ["general"];
            }

            // Dev.to tags must be lowercase, only alphanumeric characters (no hyphens, spaces, or special chars)
            const cleanTags = postData.tags
                .slice(0, 4) // Dev.to allows max 4 tags
                .map((tag) => {
                    // Convert to string and clean up
                    let cleanTag = String(tag)
                        .toLowerCase()
                        .trim()
                        // Remove ALL non-alphanumeric characters (including hyphens, spaces, underscores, etc.)
                        .replace(/[^a-zA-Z0-9]/g, "")
                        // Limit length (Dev.to has tag length limits)
                        .substring(0, 30);

                    return cleanTag;
                })
                .filter((tag) => tag.length > 0 && tag.length <= 30);

            // Ensure we have at least one valid tag
            return cleanTags.length > 0 ? cleanTags : ["general"];
        })();

        // Prepare content for Dev.to (they prefer markdown)
        let bodyContent = "";
        if (postData.content) {
            // If content exists, use it (assume it's markdown)
            bodyContent = postData.content;
        } else if (postData.htmlContent) {
            // If only HTML content exists, convert basic HTML to markdown
            bodyContent = postData.htmlContent
                .replace(/

]*>(.*?)<\/h1>/gi, "# $1\n\n") .replace(/

]*>(.*?)<\/h2>/gi, "## $1\n\n") .replace(/

]*>(.*?)<\/h3>/gi, "### $1\n\n") .replace(/

]*>(.*?)<\/p>/gi, "$1\n\n") .replace(/
/gi, "\n") .replace(/]*>(.*?)<\/strong>/gi, "**$1**") .replace(/]*>(.*?)<\/em>/gi, "*$1*") .replace(/]*>(.*?)<\/code>/gi, "`$1`") .replace(/<[^>]*>/g, "") // Remove any remaining HTML tags .replace(/\n\s*\n\s*\n/g, "\n\n") // Clean up excessive newlines .trim(); } // Add footer with original publication link const footerText = `\n\n---\n\n*Originally published on [iHateReading](https://ihatereading.in/t/${encodeURIComponent( title.replace(/\s+/g, "-") )})*`; bodyContent += footerText; const articleData = { article: { title: "postData.title || title," body_markdown: bodyContent, tags: processedTags, published: true, series: postData.series || null, canonical_url: postData.canonicalUrl || null, description: "postData.description || \"\"," cover_image: postData.coverImage || null, main_image: postData.mainImage || null, }, }; // Post to Dev.to API const devtoResponse = await fetch("https://dev.to/api/articles", { method: "POST", headers: { "Content-Type": "application/json", "api-key": process.env.DEV_TO_API_TOKEN, }, body: JSON.stringify(articleData), }); if (!devtoResponse.ok) { const errorData = await devtoResponse.text(); console.error("Dev.to API error:", errorData); console.error("Response status:", devtoResponse.status); console.error( "Response headers:", Object.fromEntries(devtoResponse.headers.entries()) ); // Try to parse error for better error messages let errorMessage = "Failed to post to Dev.to"; try { const parsedError = JSON.parse(errorData); if (parsedError.error) { errorMessage = `Dev.to API Error: ${parsedError.error}`; } } catch (e) { errorMessage = `Dev.to API Error (${devtoResponse.status}): ${errorData}`; } throw new Error(errorMessage); } const responseData = await devtoResponse.json(); // Update Firestore document with Dev.to post information await firestore.collection("publish").doc(postDoc.id).update({ devtoPostId: responseData.id, devtoUrl: responseData.url, devtoPublishedAt: new Date(), lastUpdated: new Date(), }); return c.json({ success: true, message: "Post published successfully to Dev.to", data: { devtoPostId: responseData.id, devtoUrl: responseData.url, title: "responseData.title," publishedAt: responseData.published_at, }, }); } catch (error) { console.error("Error posting to Dev.to:", error); c.status = 500; return c.json({ error: error.message, }); } });

This endpoint is my simple automation API that takes a blog from our website, iHateReading, using the title of the blog and puts the content on dev.to the dev platform.

For newcomers, I write content and distribute it on multiple platforms such as Medium, Devto and Hashnode and iHatereading

But every time, I've to push content on each platform by copy-pasting, because I have made a method to convert content into HTML and markdown, so that I can go to the website and simply copy-paste on devto.

But this again takes time, and I was bored with not doing it manually and ended up making a Honojs endpoint to simply put content on automation.

For the API key to post on devto, go to devto account page and grab the integration token.

One can do the same for Medium and Hashnode, and Twitter at the same time. Make sure to parse the data correctly for each of the platforms, for example, tags in the devto platform don't allow non-alphanumeric characters and so on.

Customisation is added in the end before posting content on devto. Add originally published on {website blog link} in the end.

We can use the AI LLM after fetching content from the database, firestore in our case, to recreate the content or rewrite it and then post it on a specific platform. But I want originality to sustain so no AI layer is added.

One can use these APIs and create a SAAS tool allows other devs to post content on devto, Medium and other platforms from one single source of editor or database.

That's it for today

See you in the next one

iHateReading


Originally published on iHateReading

Top comments (0)