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.
nuxt-themes / alpine
The minimalist blog theme, powered by Nuxt & Markdown.
Alpine
The minimalist blog theme, powered by Nuxt.
Features
- Start from a profile page, scale to a complete blog!
- An open source blog theme powered by Nuxt Content, editable from Nuxt Studio.
- Write pages in Markdown and Vue components with the MDC syntax.
- Use 30+ built-in components in your Markdown pages.
Quick Start
npx nuxi@latest init -t themes/alpine
Contributing 🙏
- Clone this repository
- Install dependencies using
pnpm install
- Run
pnpm prepare
to generate type stubs. - Use
pnpm dev
to start playground in development mode.
License
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
Development Server
Start the development server on http://localhost:3000
npm run dev
Production
Build the application for production:
npm run build
Locally preview production build:
npm run preview
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 @koutsWhat 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");
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,
);
});
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]}`);
}
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;
});
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
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,
);
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();
});
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)