DEV Community

Cover image for dev.to API: How to Turn DEV Posts into Postcards ๐Ÿ“ซ
Dan Harding
Dan Harding

Posted on

dev.to API: How to Turn DEV Posts into Postcards ๐Ÿ“ซ

I gain a lot from the DEV community. Whether it's useful tips, project ideas or just learning from other people's experiences. It's the only place I know that offers an equal platform for bloggers and programmers of all abilities, blended in a way that makes accessing interesting and relevant content seem simple.

But for it to exist, the community relies on users who are active in sharing content that's new and engaging. This isn't an easy thing to do, and after almost 2 years as a member, it took until February before I felt confident enough to post publicly. However, once it was done, the positive reaction I received gave me the encouragement to write reflectively more often.

Whilst my work involves tech, I'm not a developer. In fact, the majority of my professional network sits within education, making it less likely for colleagues or peers to encounter (or follow links to) the DEV site. So as I continued to write, it felt increasingly important to find a way of sharing content more flexibly, and one that reaches the broadest possible audience. Twitter helps, but how can DEV content be disguised to appear less 'technical'?

Answer: The dev.to API. ๐ŸŽ‰

But there's a catch. As far as I'm aware, the dev.to API is still experimental. Nevertheless, despite no official documentation, posts from other users provided enough information to test it out.

By adding a username parameter to the base URL (e.g. https://dev.to/api/articles?username=devteam), the API returns a JSON file containing metadata related to the articles from a specified user. After 'fetching' the data with a basic fetch() method, it's then available to manipulate with JavaScript and add to the DOM.

The snippet below shows how this works, printing the JSON to the console:

    <script>
        fetch('https://dev.to/api/articles?username=devteam')
        .then(function(response) {
            return response.json();
        })
        .then(function(myJson) {
            console.log(myJson)
        });
    </script>
Enter fullscreen mode Exit fullscreen mode

For me, this offers a perfect solution for adding DEV content to other sites (including my own), with extra flexibility for defining how it appears. For example, the CodePen below shows how each article can be presented as an individual card, styled in a way that matches the containing site. But also by using CSS Grid for the target div, any amount of posts can be added to the page whilst keeping the layout responsive.

If you'd like to see how the API can be used to create a blog section of a portfolio website, please visit danharding.co.uk or feel free to play with the CodePen.

And if you have any feedback, I'm always interested to hear it! ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป

Top comments (7)

Collapse
 
picocreator profile image
Eugene Cheah • Edited

Next step: Make it physical - integrate with lob.com and send them out in hardcopies as physical postcards haha.

Reminded by

Collapse
 
hanzla-baig profile image
Hanzla Baig

<!DOCTYPE html>









Beautiful Dev.to Post Fetcher

<br>
/* General Reset */<br>
* {<br>
margin: 0;<br>
padding: 0;<br>
box-sizing: border-box;<br>
}</p>
<div class="highlight"><pre class="highlight plaintext"><code> body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f4f8;
color: #333;
overflow-x: hidden;
}
/* Header */
header {
    background: linear-gradient(135deg, #6a11cb, #2575fc);
    color: #fff;
    text-align: center;
    padding: 1.5rem 1rem;
    font-size: 2rem;
    font-weight: bold;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

/* Loading Spinner */
.spinner {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50vh;
}

.spinner::after {
    content: "";
    width: 50px;
    height: 50px;
    border: 6px solid #ddd;
    border-top-color: #007bff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* Post Container */
.post-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 25px;
    padding: 30px;
}

.post-card {
    background: #fff;
    width: 350px;
    border-radius: 15px;
    overflow: hidden;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    transition: all 0.3s ease-in-out;
    animation: fadeIn 0.8s ease-in;
    cursor: pointer;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.post-card:hover {
    transform: scale(1.05);
}

.post-card img {
    width: 100%;
    height: 180px;
    object-fit: cover;
}

.post-card .content {
    padding: 15px;
}

.post-card h3 {
    font-size: 1.4rem;
    margin-bottom: 10px;
    color: #333;
}

.post-card p {
    color: #666;
    font-size: 1rem;
    margin-bottom: 15px;
}

.stats {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.9rem;
    color: #777;
}

.stats span {
    display: flex;
    align-items: center;
    gap: 5px;
}

.stats span i {
    color: #007bff;
}

.read-more {
    text-align: center;
    padding: 10px;
    background: #007bff;
    color: #fff;
    font-weight: bold;
    text-decoration: none;
    display: block;
    border-radius: 0 0 15px 15px;
    transition: background 0.3s;
}

.read-more:hover {
    background: #0056b3;
}
Enter fullscreen mode Exit fullscreen mode

&lt;/style&gt;
</code></pre></div>
<p></head><br>
<body><br>
<header>๐ŸŒŸ Dev.to Advanced Post Fetcher ๐ŸŒŸ</header><br>
<div class="spinner" id="spinner"></div><br>
<div class="post-container" id="postContainer" style="display: none;"></div></p>
<div class="highlight"><pre class="highlight plaintext"><code>&lt;script&gt;
const username = "hanzla-baig"; // Replace with your Dev.to username
const postContainer = document.getElementById("postContainer");
const spinner = document.getElementById("spinner");

// Fetch posts using Dev.to API
async function fetchPosts() {
    const url = `https://dev.to/api/articles?username=${username}`;

    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Failed to fetch posts");

        const posts = await response.json();
        displayPosts(posts);
    } catch (error) {
        spinner.innerHTML = `&amp;lt;p style="color: red;"&amp;gt;Error: ${error.message}&amp;lt;/p&amp;gt;`;
    }
}

// Display posts
function displayPosts(posts) {
    spinner.style.display = "none";
    postContainer.style.display = "flex";

    posts.forEach(post =&amp;gt; {
        const card = document.createElement("div");
        card.classList.add("post-card");

        card.innerHTML = `
            &amp;lt;img src="${post.cover_image || 'https://via.placeholder.com/350x180'}" alt="Post Cover"&amp;gt;
            &amp;lt;div class="content"&amp;gt;
                &amp;lt;h3&amp;gt;${post.title}&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;${post.description || 'No description available.'}&amp;lt;/p&amp;gt;
                &amp;lt;div class="stats"&amp;gt;
                    &amp;lt;span&amp;gt;&amp;lt;i&amp;gt;โค๏ธ&amp;lt;/i&amp;gt; ${post.positive_reactions_count} Likes&amp;lt;/span&amp;gt;
                    &amp;lt;span&amp;gt;&amp;lt;i&amp;gt;๐Ÿ’ฌ&amp;lt;/i&amp;gt; ${post.comments_count} Comments&amp;lt;/span&amp;gt;
                    &amp;lt;span&amp;gt;&amp;lt;i&amp;gt;โฑ๏ธ&amp;lt;/i&amp;gt; ${post.reading_time_minutes} min read&amp;lt;/span&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;a href="${post.url}" target="_blank" class="read-more"&amp;gt;Read More&amp;lt;/a&amp;gt;
        `;
        postContainer.appendChild(card);
    });
}

// Initialize the fetch process
fetchPosts();
Enter fullscreen mode Exit fullscreen mode

&lt;/script&gt;
</code></pre></div>
<p></body><br>
</html></p>

<p>Simply use this to show your posts into any webdite</p>

Collapse
 
lukewduncan profile image
Luke Duncan

Dude awesome work!

Collapse
 
johnkazer profile image
John Kazer

Thanks, very useful :-)

Collapse
 
dotnetcoreblog profile image
Jamie

I did something very similar for my company website a week ago, but I didn't style them very well. But this part has made me want to take another whack at it.

Collapse
 
ctg123 profile image
Chaance T. Graves

Hey Dan,

This is legit! Would like to see how the progress is made with this.

Collapse
 
karaluton profile image
Kara Luton

Hey Dan, I tried doing this but am getting a CORS error in my console. Do I need to add the API key somewhere and if so where?