DEV Community

Cover image for Exploring the DEV.to API to Build a Blog
LFXA
LFXA

Posted on • Updated on • Originally published at lfxa.vercel.app

Exploring the DEV.to API to Build a Blog

Hello, folks! Today I'm going to share with you how I set up a blog page using the Dev.to API. This is a great way to expand the reach of your content. Additionally, I write the same post in both Portuguese and English to reach more people, and with that, there's a challenge of implementing localization on the blog so that the same post works in both languages.

Before discussing the API usage, I want to mention three references that helped me set up my blog page. Keeping in mind the concept that nothing is created, only combined, I drew inspiration from two blog themes for Nuxt 3 and a post on creating a skeleton loader with Tailwind CSS.

Alpine has a pleasant post layout, always highlighting the most recent one, while Nuxt Starter Blog provides a simple and effective way to handle pagination for numerous posts.

GitHub logo nuxt-themes / alpine

The minimalist blog theme, powered by Nuxt & Markdown.

Alpine

Alpine

npm version License npm downloads Nuxt Nuxt Studio Volta

The minimalist blog theme, powered by Nuxt.

Features

Quick Start

npx nuxi@latest init -t themes/alpine
Enter fullscreen mode Exit fullscreen mode

Contributing πŸ™

  1. Clone this repository
  2. Install dependencies using pnpm install
  3. Run pnpm prepare to generate type stubs.
  4. Use pnpm dev to start playground in development mode.

License

MIT



GitHub logo narasimhajupally / tailwind-nuxtjs-starter-blog

This is a Nuxt.js, Tailwind CSS blogging starter template. Comes out of the box configured with the latest technologies to make technical writing a breeze. Easily configurable and customizable. Perfect as a replacement to existing Jekyll and Hugo individual blogs.

Tailwind Nextjs Starter Blog (work in progress)

Inspired by tailwind-nextjs-starter-blog

Look at the Nuxt 3 documentation to learn more.

Setup

Make sure to install the dependencies:

# npm
npm install
Enter fullscreen mode Exit fullscreen mode

Development Server

Start the development server on http://localhost:3000

npm run dev
Enter fullscreen mode Exit fullscreen mode

Production

Build the application for production:

npm run build
Enter fullscreen mode Exit fullscreen mode

Locally preview production build:

npm run preview
Enter fullscreen mode Exit fullscreen mode

Check out the deployment documentation for more information.






I liked the idea of combining the two. Thus, I could create a unique experience that combines the elegant look of Alpine with the navigational ease of Nuxt Starter Blog.

Thanks @kouts

What you'll need:

  • The Dev.to API, which facilitates fetching posts and tags for your blog.
  • Tools like Nuxt.js and Vue-i18n to handle site localization and switch between posts in different languages.
  • Basic knowledge of HTML, CSS, and JavaScript to manipulate the data returned by the API and create the structure of your blog page.

Step by step:

Fetch posts and tags from the Dev.to API:

I used the API to retrieve posts written by you and the tags associated with each post. I used my username as a query parameter for this call, along with the state "all", ensuring that the response returns a maximum of 1000 posts with associated tags, instead of the default 30. (I hope to write at least 500 posts πŸ˜…)

     const { data: posts, pending } = await useLazyFetch(
        "https://dev.to/api/articles?username=lfxa&state=all");
Enter fullscreen mode Exit fullscreen mode

Work with site localization and manipulate the data returned by the API:

Now comes the interesting part: to make this functional, the post needs to be available in both English and Portuguese on dev.to. To determine if the post is in one of the languages, I use the "canonical_url" field with the location in the URL (e.g., https://lfxa.vercel.app/en-US/blog?to=um-portfolio-de-um-engenheiro-de-software-162o,a-portfolio-of-a-software-engineer-3i47). This field indicates the URL where the post was originally published and is redirected to my site. I apply a filter to display only posts in the desired location.

   const route = useRoute();
    const { locale } = useI18n();

    const filteredBlogPosts = computed(() => {
      return props.posts
        ?.filter((post) => {
            // location in url
          const postLang = post.canonical_url.split("/")[3];      
          const isSameLanguage = postLang === locale.value;

          //filter tag and location
          const hasTagQuery = route.query.tag !== undefined;
          return isSameLanguage &&
               (!hasTagQuery || post.tag_list.includes(route.query.tag));
        })
        // pagination
        .slice(
          props.postPerPage * (currentPage.value - 1),
          currentPage.value * props.postPerPage,
        );
    });
Enter fullscreen mode Exit fullscreen mode

As the post was originally published on dev.to, I redirect if someone clicks the link that leads directly to my site. (needs improvement if more site locations are added πŸ˜‰).

    // redirect to post in certain location
    if (route.query.to !== undefined) {
      const toPost = route.query.to.split(",");
      const index = locale.value === "pt-BR" ? 0 : 1;
      await navigateTo(`/blog/${toPost[index]}`);
    }
Enter fullscreen mode Exit fullscreen mode

I filtered the tags to show them in their respective location:

    const tags = computed(() => {
      const tagCounts = {};
      props.posts?.forEach((post) => {
        const lang = post.canonical_url.split("/")[3];
        post.tag_list.forEach((tag) => {
          const key = tag + ":" + lang;
          if (tagCounts[key]) {
            tagCounts[key]++;
          } else {
            tagCounts[key] = 1;
          }
        });
      });

      const output = [];
      for (const tagLang in tagCounts) {
        const key = tagLang.split(":");
        output.push({ name: key[0], quantity: tagCounts[tagLang], lang: key[1] });
      }
      output.sort((a, b) => a.name.localeCompare(b.name));
      return output;
    });
Enter fullscreen mode Exit fullscreen mode

and I also added a filter for tags that are selected and become query parameters:

    // v-if
    tag.lang === $i18n.locale;

    // nuxtLink to
    route.fullPath.includes(tag.name) ? 'blog' : 'blog?tag=' + tag.name
Enter fullscreen mode Exit fullscreen mode

Create the structure of the blog post page:

To get the post content, it's necessary to query the API specifically for that particular post. I used the post's slug along with my username as query parameters in the API. The post content is provided in the "body_html" field, maintaining the CSS classes from the dev.to site. To ensure proper post viewing, I copied part of the CSS from the site that makes sense. I recommend avoiding the use of the v-html directive as it is sensitive to XSS attacks (Cross-Site Scripting). (I hope dev.to doesn't transmit malicious scripts that can be executed on users' sites).

       const { data: post, pending } = await useFetch(
      "https://dev.to/api/articles/lfxa/" + slug,
    );
Enter fullscreen mode Exit fullscreen mode

Additionally, if there's a language change on the page, I make a new call for the same post in the other language:

    onBeforeRouteLeave(async (to, from, next) => {
      if (
        post.value &&
        to.fullPath.split("/").pop() === from.fullPath.split("/").pop()
      ) {
        const last = post.value.canonical_url.split("/").pop();
        const toPost = last.replace("blog?to=", "").split(",");
        const index = locale.value === "pt-BR" ? 0 : 1;
        await next(`/${locale.value}/blog/${toPost[index]}`);
      }
      next();
    });
Enter fullscreen mode Exit fullscreen mode

If you're interested, you can see the source code on GitHub here.


Conclusion:

Sharing what we know is essential to make learning more accessible and promote the growth of other developers. Additionally, by sharing knowledge, we strengthen our own skills, see what can be improved, and contribute to the advancement of the software development community as a whole.

In the same way, let your light shine before others, that they may see your good deeds and glorify your Father in heaven. Matthew 5:16

Top comments (0)